SystemOrganization addCategory: #'TextLint-Seaside'! !TLSyntacticElement methodsFor: '*textlint-seaside' stamp: 'lr 11/3/2010 19:54'! renderStart: aFailure on: html html anchor name: aFailure element start printString; with: [ ]. html document openTag: 'a' attributes: (WAHtmlAttributes new at: 'class' put: 'problem'; at: 'href' put: '#' , (aFailure rule class name allButFirst: 2); at: 'title' put: aFailure rule name , ': ' , aFailure rule rationale; yourself) closed: false! ! !TLSyntacticElement methodsFor: '*textlint-seaside' stamp: 'lr 10/26/2010 13:24'! renderStarts: starts stops: stops on: html [ starts notEmpty and: [ starts first element start <= self start ] ] whileTrue: [ self renderStart: starts removeFirst on: html ]. self renderTextOn: html. [ stops notEmpty and: [ stops first element stop <= self stop ] ] whileTrue: [ self renderStop: stops removeFirst on: html ]! ! !TLSyntacticElement methodsFor: '*textlint-seaside' stamp: 'lr 11/3/2010 15:24'! renderStop: aFailure on: html html document closeTag: 'a'! ! !TLSyntacticElement methodsFor: '*textlint-seaside' stamp: 'lr 10/26/2010 13:24'! renderTextOn: html html text: self text! ! Error subclass: #TLTextLintError instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'TextLint-Seaside'! !TLElement methodsFor: '*textlint-seaside' stamp: 'lr 10/25/2010 20:51'! renderStarts: starts stops: stops on: html self children do: [ :each | each renderStarts: starts stops: stops on: html ]! ! WAComponent subclass: #TLInputComponent instanceVariableNames: 'input' classVariableNames: '' poolDictionaries: '' category: 'TextLint-Seaside'! !TLInputComponent methodsFor: 'actions' stamp: 'lr 5/11/2011 20:30'! analyze self show: (TLResultComponent on: (TLRootComponent analyze: input))! ! !TLInputComponent methodsFor: 'actions' stamp: 'lr 5/11/2011 20:59'! customize self show: (TLCustomComponent on: input)! ! !TLInputComponent methodsFor: 'initialization' stamp: 'lr 5/11/2011 20:19'! initialize super initialize. input := 'Please replace with the text you wish to analyze ...'! ! !TLInputComponent methodsFor: 'rendering' stamp: 'lr 9/4/2011 15:25'! renderButtonsOn: html html paragraph class: 'wait'; style: 'display: none;'; with: 'Please wait while your text is being analyzed ...'. html paragraph class: 'buttons'; with: [ html submitButton onClick: (html jQuery: 'p.buttons') hide , (html jQuery: 'p.wait') show; callback: [ self analyze ]; with: 'Analyze Text'. html submitButton onClick: (html jQuery: 'p.buttons') hide , (html jQuery: 'p.wait') show; callback: [ self customize ]; with: 'Custom Search' ]! ! !TLInputComponent methodsFor: 'rendering' stamp: 'lr 5/11/2011 19:56'! renderContentOn: html self renderIntroOn: html. self renderTextOn: html. self renderButtonsOn: html! ! !TLInputComponent methodsFor: 'rendering' stamp: 'lr 9/4/2011 15:31'! renderIntroOn: html html paragraph: 'TextLint is a tool to check your scientific writing for common style errors. To give it a try paste your plain text, HTML or LaTeX source into the input field below and click on "Analyze Text".'! ! !TLInputComponent methodsFor: 'rendering' stamp: 'lr 5/11/2011 20:33'! renderTextOn: html html div: [ html textArea value: input; script: (html jQuery this triggerFocus; triggerSelect); callback: [ :value | input := value ] ]! ! WAComponent subclass: #TLResultComponent instanceVariableNames: 'checker' classVariableNames: '' poolDictionaries: '' category: 'TextLint-Seaside'! TLResultComponent subclass: #TLCustomComponent instanceVariableNames: 'query pattern error' classVariableNames: '' poolDictionaries: '' category: 'TextLint-Seaside'! !TLCustomComponent class methodsFor: 'instance creation' stamp: 'lr 5/11/2011 20:36'! on: aString ^ self new initializeOn: aString! ! !TLCustomComponent methodsFor: 'accessing' stamp: 'lr 9/5/2011 17:57'! cookieKey ^ 'tlquery' ! ! !TLCustomComponent methodsFor: 'accessing' stamp: 'lr 9/5/2011 19:44'! defaultQuery | cookie | cookie := self requestContext request cookieAt: self cookieKey. ^ cookie isNil ifTrue: [ 'the {adjective} {noun}' ] ifFalse: [ cookie value ]! ! !TLCustomComponent methodsFor: 'accessing' stamp: 'lr 9/5/2011 19:54'! emptyQuery ^ 'Please specify a query in the search box'! ! !TLCustomComponent methodsFor: 'testing' stamp: 'lr 9/5/2011 17:58'! hasError ^ error notNil! ! !TLCustomComponent methodsFor: 'initialization' stamp: 'lr 9/4/2011 13:30'! initializeOn: aString self query: self defaultQuery. super initializeOn: (TLRootComponent analyze: aString with: (Array with: self))! ! !TLCustomComponent methodsFor: 'accessing' stamp: 'lr 9/5/2011 18:06'! message ^ checker results isEmpty ifTrue: [ 'TextLint could not discover any matches in your text.' ] ifFalse: [ 'TextLint discovered ' , (checker results size printString) , ' match' , (checker results size > 1 ifTrue: [ 'es' ] ifFalse: [ '' ]) , ' in your text.' ]! ! !TLCustomComponent methodsFor: 'rule' stamp: 'lr 9/4/2011 13:31'! name ^ 'Custom query'! ! !TLCustomComponent methodsFor: 'accessing' stamp: 'lr 9/5/2011 19:53'! query: aString query := aString. aString isEmptyOrNil ifTrue: [ ^ error := self emptyQuery ]. pattern := TLPatternTokenizer parse: query. pattern isPetitFailure ifTrue: [ ^ error := pattern message asCapitalizedPhrase , ' at ' , pattern position asString ]. error := nil. self requestContext response addCookie: (WACookie key: self cookieKey value: query). TLRootComponent log: 'custom' data: query. checker isNil ifFalse: [ [ checker reset; results ] valueWithin: TLRootComponent maximumTime onTimeout: [ ^ TLTextLintError signal: 'Input took too long to process.' ] ]! ! !TLCustomComponent methodsFor: 'rule' stamp: 'lr 9/4/2011 13:24'! rationale ^ query! ! !TLCustomComponent methodsFor: 'rendering' stamp: 'lr 9/5/2011 19:19'! renderContentOn: html self renderQueryOn: html. self hasError ifTrue: [ self renderErrorOn: html; renderButtonsOn: html ] ifFalse: [ super renderContentOn: html ]! ! !TLCustomComponent methodsFor: 'rendering' stamp: 'lr 9/5/2011 19:20'! renderErrorOn: html html div class: 'error'; with: [ html paragraph: 'Error: ' , error , '.' ]. html div: [ html unorderedList: [ html listItem: 'A word matches any case-insensitive occurrence of that word.'. html listItem: 'A punctuation character matches any occurrence of that punctuation character.'. html listItem: 'A whitespace matches any (possibly empty) whitespace or markup sequence.'. html listItem: [ html text: 'A word in curly braces matches any word of the specified type:'; break. TLWordClassifier types asSortedCollection do: [ :each | html text: '{' , each , '}' ] separatedBy: [ html text: ', ' ]. html text: '.' ] ] ]! ! !TLCustomComponent methodsFor: 'rendering' stamp: 'lr 9/5/2011 18:10'! renderQueryOn: html html div class: 'query'; with: [ html textInput text: query; script: (html jQuery this triggerFocus; triggerSelect); callback: [ :value | self query: value ]. html submitButton with: 'Search' ]! ! !TLCustomComponent methodsFor: 'rule' stamp: 'lr 9/4/2011 12:46'! runOnDocument: aDocument ^ #()! ! !TLCustomComponent methodsFor: 'rule' stamp: 'lr 9/4/2011 12:46'! runOnParagraph: aParagraph ^ #()! ! !TLCustomComponent methodsFor: 'rule' stamp: 'lr 9/4/2011 12:53'! runOnSentence: aSentence ^ (pattern matchesIn: aSentence children) collect: [ :each | TLRuleFailure on: self at: (TLPhrase withAll: each) ]! ! !TLCustomComponent methodsFor: 'rule' stamp: 'lr 9/4/2011 13:04'! runOnWord: aWord ^ #()! ! !TLCustomComponent methodsFor: 'rendering' stamp: 'lr 9/4/2011 15:36'! updateRoot: aHtmlRoot super updateRoot: aHtmlRoot. aHtmlRoot bodyAttributes addClass: 'custom'! ! !TLResultComponent class methodsFor: 'instance creation' stamp: 'lr 5/11/2011 20:15'! on: aChecker ^ self new initializeOn: aChecker! ! !TLResultComponent methodsFor: 'initialization' stamp: 'lr 5/11/2011 20:16'! initializeOn: aChecker checker := aChecker! ! !TLResultComponent methodsFor: 'accessing' stamp: 'lr 9/5/2011 18:05'! message ^ checker results isEmpty ifTrue: [ 'Congratulations!! TextLint could not discover any issues in your text.' ] ifFalse: [ 'TextLint discovered ' , (checker results size printString) , ' possible issue' , (checker results size > 1 ifTrue: [ 's' ] ifFalse: [ '' ]) , ' in your text. Click an issue in the list to navigate to your text; click an issue in the text to navigate to an explanation of the issue.' ]! ! !TLResultComponent methodsFor: 'rendering' stamp: 'lr 5/11/2011 20:18'! renderButtonsOn: html. html paragraph class: 'buttons'; with: [ html submitButton callback: [ self answer ]; with: 'Go Back' ]! ! !TLResultComponent methodsFor: 'rendering' stamp: 'lr 5/11/2011 20:18'! renderContentOn: html self renderIntroOn: html. self renderResultOn: html. self renderButtonsOn: html! ! !TLResultComponent methodsFor: 'rendering' stamp: 'lr 9/5/2011 18:04'! renderIntroOn: html html paragraph: self message! ! !TLResultComponent methodsFor: 'rendering' stamp: 'lr 9/4/2011 11:19'! renderListOn: html | groups | html unorderedList: [ groups := checker results groupedBy: [ :each | each rule class ]. groups := groups values asOrderedCollection sorted: [ :a :b | a first rule name < b first rule name ]. groups do: [ :failures | | rule | rule := failures first rule. html listItem: [ html heading level: 3; with: [ html anchor name: (rule class name allButFirst: 2). html text: rule name ]. html paragraph: [ html withLineBreaks: rule rationale ]. html orderedList: [ failures do: [ :failure | html listItem: [ html anchor url: '#' , failure element start printString; with: (failure element text truncate: 100) ] ] ] ] ] ]! ! !TLResultComponent methodsFor: 'rendering' stamp: 'lr 9/5/2011 18:14'! renderResultOn: html | starts stops | starts := checker results asOrderedCollection sorted: [ :a :b | a element start < b element start ]. stops := checker results asOrderedCollection sorted: [ :a :b | a element stop < b element stop ]. checker results isEmpty ifFalse: [ html div class: 'list'; with: [ self renderListOn: html ] ]. html div class: 'result'; with: [ checker document renderStarts: starts stops: stops on: html ]! ! WAComponent subclass: #TLRootComponent instanceVariableNames: 'content' classVariableNames: '' poolDictionaries: '' category: 'TextLint-Seaside'! !TLRootComponent class methodsFor: 'utilities' stamp: 'lr 5/11/2011 20:10'! analyze: aString ^ self analyze: aString with: TLWritingStyle scientificPaperStyle rules! ! !TLRootComponent class methodsFor: 'utilities' stamp: 'lr 9/5/2011 17:42'! analyze: aString with: aCollectionOfRules | checker normalized | checker := TLTextLintChecker new. aCollectionOfRules do: [ :each | checker addRule: each ]. normalized := aString first: (aString size min: self maximumSize). self log: 'analyze' data: normalized. [ checker parse: normalized; results ] valueWithin: self maximumTime onTimeout: [ ^ TLTextLintError signal: 'Input took too long to process.' ]. aCollectionOfRules isEmpty ifFalse: [ self log: 'results' data: (String streamContents: [ :s | (checker results inject: Bag new into: [ :bag :each | bag add: each rule; yourself ]) valuesAndCounts keysAndValuesDo: [ :rule :count | s print: count; nextPut: $ ; nextPutAll: rule name; cr ] ]) ]. ^ checker! ! !TLRootComponent class methodsFor: 'initialization' stamp: 'lr 9/5/2011 17:38'! initialize | application | application := WAAdmin register: self asApplicationAt: 'textlint'. application preferenceAt: #serverPath put: '/'. application addLibrary: JQGoogleLibrary. WAAdmin defaultDispatcher defaultName: 'textlint'. WAAdmin applicationDefaults removeParent: WADevelopmentConfiguration instance. WAAdmin applicationExceptionHandlingDefaults at: #exceptionHandler put: WAErrorHandler! ! !TLRootComponent class methodsFor: 'initialization' stamp: 'lr 9/5/2011 17:44'! initializeDevelopment WAAdmin applicationDefaults addParent: WADevelopmentConfiguration instance. WAAdmin applicationExceptionHandlingDefaults at: #exceptionHandler put: WADebugErrorHandler! ! !TLRootComponent class methodsFor: 'utilities' stamp: 'lr 9/4/2011 14:20'! log: aLabelString data: aDataString | directory context | directory := (FileDirectory default directoryNamed: 'logs') directoryNamed: (Date today printFormat: #(3 2 1 $- 1 1 2)). directory assureExistence. context := WACurrentRequestContext signal. directory fileNamed: context session key , '.log' do: [ :stream | stream setToEnd. aLabelString isEmptyOrNil ifFalse: [ stream nextPutAll: ' === '; print: TimeStamp now. stream nextPutAll: ' === '; nextPutAll: aLabelString. stream nextPutAll: ' === '; cr. aDataString isEmptyOrNil ifFalse: [ stream nextPutAll: aDataString; cr; cr ] ] ]! ! !TLRootComponent class methodsFor: 'configuration' stamp: 'lr 5/11/2011 20:06'! maximumSize "200k should be enough for most papers." ^ 1024 * 1024 // 4! ! !TLRootComponent class methodsFor: 'configuration' stamp: 'lr 9/5/2011 19:52'! maximumTime "If this takes more than 20 seconds there is something wrong." ^ 20 seconds! ! !TLRootComponent methodsFor: 'accessing' stamp: 'lr 9/4/2011 15:36'! children ^ Array with: content! ! !TLRootComponent methodsFor: 'initialization' stamp: 'lr 9/4/2011 14:22'! initialize super initialize. content := TLInputComponent new! ! !TLRootComponent methodsFor: 'rendering' stamp: 'lr 5/11/2011 19:51'! renderBodyOn: html html form: content! ! !TLRootComponent methodsFor: 'rendering' stamp: 'lr 5/11/2011 19:50'! renderContentOn: html html div class: 'head'; with: [ self renderHeadOn: html ]. html div class: 'body'; with: [ self renderBodyOn: html ]. html div class: 'foot'; with: [ self renderFootOn: html ]! ! !TLRootComponent methodsFor: 'rendering' stamp: 'lr 10/26/2010 21:40'! renderFootOn: html html paragraph: [ html anchor url: 'http://scg.unibe.ch/research/textlint'; with: 'TextLint'. html text: ' is a '. html anchor url: 'http://scg.unibe.ch/staff/fabrizioperin/'; with: 'Fabrizio Perin'. html text: ', '. html anchor url: 'http://www.lukas-renggli.ch/'; with: 'Lukas Renggli'. html text: ', and '. html anchor url: 'http://www.jorgeressia.com/'; with: 'Jorge Ressia'. html text: ' production.'. html break. html text: 'Powered by '. html anchor url: 'http://www.pharo-project.org/'; with: 'Pharo Smalltalk'. html text: ', '. html anchor url: 'http://www.seaside.st/'; with: 'Seaside'. html text: ', and '. html anchor url: 'http://www.mirandabanda.org/'; with: 'Cog'. html text: '.' ]! ! !TLRootComponent methodsFor: 'rendering' stamp: 'lr 5/11/2011 19:51'! renderHeadOn: html html heading level: 1; with: [ html span: self title ]! ! !TLRootComponent methodsFor: 'accessing' stamp: 'lr 9/5/2011 19:15'! style ^ '.head, .body, .foot { width: 800px; margin: 0 auto; } .head h1 { width: 263px; height: 100px; background-image: url("http://textlint.lukas-renggli.ch/images/logo.png"); } .head h1 span { display: none; } .body textarea, .body div.result { padding: 0; resize: none; width: 790px; height: 600px; line-height: 1.5; border: 1px solid #bbb; font: 1em "andale mono", "lucida console", monospace; } .body div.list { resize: none; overflow: auto; width: 800px; height: 200px; line-height: 1.5; border: 1px solid #bbb; border-bottom: none; } .custom .body div.list { height: 130px; } .body div.list ul { padding: 0; margin: 0; } .body div.list ul li { list-style-type: none; } .body div.list ul li h3 { margin: 0; padding: 5px; background: #eee; } .body div.list ul li p { margin: 0; padding: 5px; } .body div.list ol { margin: 0; padding: 0 0 10px 5px; } .body div.list ol li { margin: 0; padding: 0 0 0 10px; list-style-type: none; } .body div.result { overflow: auto; height: 400px; padding: 5px; white-space: pre-wrap; white-space: -moz-pre-wrap !!important; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word; } .body div.result .markup { color: gray; } .body div.result .problem { border-bottom: 1px dotted red; color: red; text-decoration: none; } .body div.result .problem:hover { background: #fbe3e4; } .body p.buttons, .body p.wait { text-align: center; padding-top: 10px; } .body p.buttons input { margin: 0 10px; } .query { position: relative; baseline: center; } .query input.text { width: 87%; margin-right: 1%; } .query input.submit { width: 10%; } .foot p { font-size: 0.8em; text-align: center; } .error p { margin: 0; }'! ! !TLRootComponent methodsFor: 'accessing' stamp: 'lr 10/25/2010 19:23'! title ^ 'TextLint'! ! !TLRootComponent methodsFor: 'updating' stamp: 'lr 9/4/2011 15:36'! updateRoot: aHtmlRoot super updateRoot: aHtmlRoot. aHtmlRoot beHtml5. aHtmlRoot title: self title. aHtmlRoot stylesheet url: 'http://textlint.lukas-renggli.ch/blueprint/screen.css'. aHtmlRoot script beJavascript; with: 'var _gaq = _gaq || [];_gaq.push(["_setAccount", "UA-2075184-10"]);_gaq.push(["_trackPageview"]);(function() {var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);})();'! ! !TLMarkup methodsFor: '*textlint-seaside' stamp: 'lr 10/26/2010 13:25'! renderTextOn: html html span class: 'markup'; with: [ super renderTextOn: html ]! ! TLRootComponent initialize!