Index: trunk/extensions/InlineEditor/InlineEditor.class.php |
— | — | @@ -15,6 +15,7 @@ |
16 | 16 | private $editWarning; /// < boolean that shows if the editWarning message of the Vector Extension is enabled |
17 | 17 | private $article; /// < Article object to edit |
18 | 18 | private $extendedEditPage; /// < ExtendedEditPage object we're using to handle editor logic |
| 19 | + private $intro; /// < Intro message(s) that should be displayed on top |
19 | 20 | |
20 | 21 | /** |
21 | 22 | * Main entry point, hooks into MediaWikiPerformAction. |
— | — | @@ -163,7 +164,7 @@ |
164 | 165 | */ |
165 | 166 | public static function ajaxPreview( $json, $pageName ) { |
166 | 167 | $title = Title::newFromText( $pageName ); |
167 | | - $article = Article::newFromId( $title->getArticleId() ); |
| 168 | + $article = new Article( $title ); |
168 | 169 | |
169 | 170 | $editor = new InlineEditor( $article ); |
170 | 171 | return $editor->preview( $json ); |
— | — | @@ -213,7 +214,7 @@ |
214 | 215 | |
215 | 216 | // try to initialise, or else return false, which will spawn an 'advanced page' notice |
216 | 217 | $this->extendedEditPage = new ExtendedEditPage( $this->article ); |
217 | | - if ( $this->extendedEditPage->initInlineEditor() ) { |
| 218 | + if ( $this->extendedEditPage->initInlineEditor( $this ) ) { |
218 | 219 | // IMPORTANT: if the page was being saved, the script has been terminated by now!! |
219 | 220 | |
220 | 221 | // have the different kind of editors register themselves |
— | — | @@ -235,6 +236,7 @@ |
236 | 237 | $this->renderInitialState( $output, $text ); |
237 | 238 | $this->renderScroll( $output, $parserOutput ); |
238 | 239 | $this->renderEditWarning( $output ); |
| 240 | + $this->renderOpenFullEditor( $output ); |
239 | 241 | |
240 | 242 | // hook into SiteNoticeBefore to display the two boxes above the title |
241 | 243 | // @todo: fix this in core, make sure that anything can be inserted above the title, outside #siteNotice |
— | — | @@ -271,6 +273,14 @@ |
272 | 274 | } |
273 | 275 | |
274 | 276 | /** |
| 277 | + * Set the intro message(s) that should be displayed on top of the page. |
| 278 | + * @param $intro String |
| 279 | + */ |
| 280 | + public function setIntro( $intro ) { |
| 281 | + $this->intro = $intro; |
| 282 | + } |
| 283 | + |
| 284 | + /** |
275 | 285 | * Add the preference in the user preferences |
276 | 286 | * @param $user |
277 | 287 | * @param $preferences |
— | — | @@ -290,7 +300,7 @@ |
291 | 301 | * |
292 | 302 | * @param $output OutputPage |
293 | 303 | */ |
294 | | - private function renderScripts( $output ) { |
| 304 | + private function renderScripts( OutputPage $output ) { |
295 | 305 | // include the required JS and CSS files |
296 | 306 | $output->addModules( array( 'jquery.inlineEditor', 'jquery.inlineEditor.editors.basic' ) ); |
297 | 307 | |
— | — | @@ -313,7 +323,7 @@ |
314 | 324 | * @param $output OutputPage |
315 | 325 | * @param $text InlineEditorText Use this text object to generate the initial state |
316 | 326 | */ |
317 | | - private function renderInitialState( $output, $text ) { |
| 327 | + private function renderInitialState( OutputPage $output, InlineEditorText $text ) { |
318 | 328 | // convert the text object into an initial state to send |
319 | 329 | $initial = InlineEditorText::initialState( $text ); |
320 | 330 | |
— | — | @@ -333,7 +343,7 @@ |
334 | 344 | * @param $output OutputPage |
335 | 345 | * @param $parserOutput ParserOutput |
336 | 346 | */ |
337 | | - private function renderScroll( $output, $parserOutput ) { |
| 347 | + private function renderScroll( OutputPage $output, ParserOutput $parserOutput ) { |
338 | 348 | $scrollAnchor = $this->getScrollAnchor( $parserOutput ); |
339 | 349 | if( $scrollAnchor !== null ) { |
340 | 350 | $output->addInlineScript( |
— | — | @@ -350,7 +360,7 @@ |
351 | 361 | * @param $output OutputPage |
352 | 362 | * @param $parserOutput ParserOutput |
353 | 363 | */ |
354 | | - private function renderEditWarning( $output ) { |
| 364 | + private function renderEditWarning( OutputPage $output ) { |
355 | 365 | if ( $this->editWarning ) { |
356 | 366 | $output->addInlineScript( |
357 | 367 | 'jQuery( document ).ready( function() { |
— | — | @@ -361,6 +371,19 @@ |
362 | 372 | } |
363 | 373 | |
364 | 374 | /** |
| 375 | + * On new pages, open the editor right away. |
| 376 | + */ |
| 377 | + private function renderOpenFullEditor( OutputPage $output ) { |
| 378 | + if ( !$this->article->exists() ) { |
| 379 | + $output->addInlineScript( |
| 380 | + 'jQuery( document ).ready( function() { |
| 381 | + jQuery.inlineEditor.show( "inline-editor-root" ); |
| 382 | + } );' |
| 383 | + ); |
| 384 | + } |
| 385 | + } |
| 386 | + |
| 387 | + /** |
365 | 388 | * Get an anchor to scroll to, or null |
366 | 389 | * @param $parserOutput ParserOutput |
367 | 390 | * @return string or null |
— | — | @@ -405,7 +428,7 @@ |
406 | 429 | * @param $siteNotice string |
407 | 430 | */ |
408 | 431 | public function siteNoticeBefore( &$siteNotice ) { |
409 | | - $siteNotice = $this->renderEditBox(); |
| 432 | + $siteNotice = $this->renderIntroBox() . $this->renderEditBox(); |
410 | 433 | return false; |
411 | 434 | } |
412 | 435 | |
— | — | @@ -431,7 +454,12 @@ |
432 | 455 | * @return string HTML |
433 | 456 | */ |
434 | 457 | private function renderEditBox() { |
435 | | - $top = wfMsgExt( 'inline-editor-editbox-top', 'parseinline' ); |
| 458 | + if( $this->article->exists() ) { |
| 459 | + $top = wfMsgExt( 'inline-editor-editbox-top', 'parseinline' ); |
| 460 | + } |
| 461 | + else { |
| 462 | + $top = wfMsgExt( 'inline-editor-editbox-top-new', 'parseinline' ); |
| 463 | + } |
436 | 464 | $top .= '<hr/>'; |
437 | 465 | |
438 | 466 | $summary = wfMsgExt( 'inline-editor-editbox-changes-question', 'parseinline' ); |
— | — | @@ -459,4 +487,10 @@ |
460 | 488 | |
461 | 489 | return Html::rawElement( 'div', array( 'class' => 'editbox' ), $form ); |
462 | 490 | } |
| 491 | + |
| 492 | + private function renderIntroBox() { |
| 493 | + if( strlen( $this->intro ) <= 0 ) return ''; |
| 494 | + |
| 495 | + return Html::rawElement( 'div', array( 'class' => 'introbox' ), $this->intro ); |
| 496 | + } |
463 | 497 | } |
Index: trunk/extensions/InlineEditor/InlineEditorRoot.class.php |
— | — | @@ -1,197 +1,10 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | | - * This class provides a rootnode for a tree which provides a good structure for markings. |
5 | | - * It is closely connected to the wikitext, and should be recreated whenever a marking or |
6 | | - * wikitext changes. InlineEditorNode inherits from this class. |
| 4 | + * This is a special marking that spans all wikitext. |
7 | 5 | */ |
8 | | -class InlineEditorRoot extends InlineEditorPiece { |
9 | | - protected $wiki; /// < reference to the original wikitext |
10 | | - protected $children; /// < array of children (InlineEditorNode) |
11 | | - protected $isSorted; /// < bool whether or not the children are sorted |
12 | | - protected $lastEnd; /// < largest endposition of children, to verify during adding the children are sortd |
13 | | - |
14 | | - /** |
15 | | - * @param $wiki String Reference to the original wikitext |
16 | | - */ |
17 | | - public function __construct( &$wiki ) { |
18 | | - $this->wiki = &$wiki; |
19 | | - $this->children = array(); |
20 | | - $this->lastEnd = 0; |
21 | | - $this->isSorted = true; |
| 6 | +class InlineEditorRoot extends InlineEditorMarking { |
| 7 | + function __construct( &$wiki ) { |
| 8 | + parent::__construct( 0, strlen( $wiki ), 'rootElement inlineEditorBasic', true, true, 0, false ); |
| 9 | + $this->id = 'inline-editor-root'; |
22 | 10 | } |
23 | | - |
24 | | - /** |
25 | | - * Always span the entire text. |
26 | | - * @return int |
27 | | - */ |
28 | | - public function getStart() { |
29 | | - return 0; |
30 | | - } |
31 | | - |
32 | | - /** |
33 | | - * Always span the entire text. |
34 | | - * @return int |
35 | | - */ |
36 | | - public function getEnd() { |
37 | | - return strlen( $this->wiki ); |
38 | | - } |
39 | | - |
40 | | - /** |
41 | | - * Always returns 'inline-editor-root', the outermost div. |
42 | | - * @return string |
43 | | - */ |
44 | | - public function getId() { |
45 | | - return 'inline-editor-root'; |
46 | | - } |
47 | | - |
48 | | - /** |
49 | | - * Get an array of children of type InlineEditorNode. |
50 | | - * @return array |
51 | | - */ |
52 | | - public function getChildren() { |
53 | | - return $this->children; |
54 | | - } |
55 | | - |
56 | | - /** |
57 | | - * Add a node to the list of children. |
58 | | - * |
59 | | - * Checks whether or not the child can be added. Returns false if it cannot add the |
60 | | - * child, and true when it can. The calling class is responsible to add the node to |
61 | | - * the innermost node, this will not be done by the function. |
62 | | - * |
63 | | - * It is recommended to add nodes from left to right, as this gives the best performance. |
64 | | - * |
65 | | - * @param $child InlineEditorNode |
66 | | - * @return bool |
67 | | - */ |
68 | | - public function addChild( InlineEditorNode $child ) { |
69 | | - // if we cannot contain the child, we cannot add it |
70 | | - if( !$this->canContain( $child ) ) return false; |
71 | | - |
72 | | - // if the start is before the largest endpoint, check all children for overlap |
73 | | - if( $child->getStart() < $this->lastEnd) { |
74 | | - foreach( $this->children as $otherChild ) { |
75 | | - if( $child->hasOverlap( $otherChild ) ) return false; |
76 | | - } |
77 | | - |
78 | | - // if there is no overlap, we're sure that the list isn't sorted anymore |
79 | | - $this->isSorted = false; |
80 | | - } |
81 | | - |
82 | | - // add the child and set the parent of the child to $this |
83 | | - $this->children[$child->getStart()] = $child; |
84 | | - $child->parent = $this; |
85 | | - |
86 | | - // move $this->lastEnd if needed |
87 | | - if( $child->getEnd() > $this->lastEnd ) $this->lastEnd = $child->getEnd(); |
88 | | - |
89 | | - return true; |
90 | | - } |
91 | | - |
92 | | - /** |
93 | | - * Find the node (or root) with the smallest length still able to contain $piece. |
94 | | - * @param $piece InlineEditorPiece |
95 | | - * @return InlineEditorPiece |
96 | | - */ |
97 | | - public function findBestParent( InlineEditorPiece $piece ) { |
98 | | - // if we cannot contain the piece, return false |
99 | | - if( !$this->canContain( $piece ) ) return false; |
100 | | - |
101 | | - // sorted children is a precondition for the algoritm |
102 | | - $this->sort(); |
103 | | - |
104 | | - foreach( $this->children as $start => $child ) { |
105 | | - // if we've move past the end of the piece, stop |
106 | | - if( $piece->getEnd() < $start ) break; |
107 | | - |
108 | | - // try to fit the piece to this child |
109 | | - if( $piece->getStart() >= $start ) { |
110 | | - $fit = $child->findBestParent( $piece ); |
111 | | - // if we found a child that fits the piece, return it |
112 | | - if( $fit !== false ) { |
113 | | - return $fit; |
114 | | - } |
115 | | - } |
116 | | - } |
117 | | - |
118 | | - // if we cannot find a suitable child, but we can contain it in this piece, return $this |
119 | | - return $this; |
120 | | - } |
121 | | - |
122 | | - /** |
123 | | - * Find the highest level of children that can be fit into a certain piece. |
124 | | - * This will return an array of nodes that are best fit. |
125 | | - * @param $piece InlineEditorPiece |
126 | | - * @return array |
127 | | - */ |
128 | | - public function findBestChildren( InlineEditorPiece $piece ) { |
129 | | - // try to find a parent that fits $piece (which can very well be $this!) |
130 | | - $parent = $this->findBestParent( $piece ); |
131 | | - |
132 | | - // if we cannot find a parent, return false |
133 | | - if( !$parent ) return false; |
134 | | - |
135 | | - // if the piece can contain the entire parent piece, just return that piece |
136 | | - if( $piece->canContain( $parent ) ) return array( $parent ); |
137 | | - |
138 | | - // sorting is a precondition of the algoritm |
139 | | - $this->sort(); |
140 | | - |
141 | | - $children = array(); |
142 | | - foreach( $parent->children as $start => $child ) { |
143 | | - // if we've moved past the end of the piece, stop |
144 | | - if( $start > $piece->getEnd() ) break; |
145 | | - |
146 | | - // add the child to the list if it can be contained in the piece |
147 | | - if( $piece->canContain( $child ) ) { |
148 | | - $children[] = $child; |
149 | | - } |
150 | | - } |
151 | | - return $children; |
152 | | - } |
153 | | - |
154 | | - /** |
155 | | - * Render the start tag, in this case always a div with id="inline-editor-root". |
156 | | - * @return string HTML |
157 | | - */ |
158 | | - protected function renderStartTag() { |
159 | | - return '<div id="inline-editor-root">' . "\n"; |
160 | | - } |
161 | | - |
162 | | - /** |
163 | | - * Render the end tag, in this case always a closing div. |
164 | | - * @return string HTML |
165 | | - */ |
166 | | - protected function renderEndTag() { |
167 | | - return "\n</div>"; |
168 | | - } |
169 | | - |
170 | | - /** |
171 | | - * Render the entire tag, with recursion on the children. |
172 | | - * @return string HTML |
173 | | - */ |
174 | | - public function render() { |
175 | | - $this->sort(); |
176 | | - $lastPos = $this->getStart(); |
177 | | - $output = $this->renderStartTag(); |
178 | | - foreach( $this->children as $child ) { |
179 | | - $output .= substr( $this->wiki, $lastPos, $child->getStart() - $lastPos ); |
180 | | - $output .= $child->render(); |
181 | | - $lastPos = $child->getEnd(); |
182 | | - } |
183 | | - |
184 | | - $output .= substr( $this->wiki, $lastPos, $this->getEnd() - $lastPos ); |
185 | | - $output .= $this->renderEndTag(); |
186 | | - |
187 | | - return $output; |
188 | | - } |
189 | | - |
190 | | - /** |
191 | | - * Sort the children by start position (key). |
192 | | - */ |
193 | | - protected function sort() { |
194 | | - if( $this->isSorted ) return; |
195 | | - ksort( $this->children ); |
196 | | - $this->isSorted = true; |
197 | | - } |
198 | | -} |
\ No newline at end of file |
| 11 | +} |
Index: trunk/extensions/InlineEditor/InlineEditorText.class.php |
— | — | @@ -27,6 +27,7 @@ |
28 | 28 | */ |
29 | 29 | public function __construct( Article $article ) { |
30 | 30 | $this->article = $article; |
| 31 | + $this->wikiOriginal = ''; |
31 | 32 | } |
32 | 33 | |
33 | 34 | /** |
— | — | @@ -57,7 +58,7 @@ |
58 | 59 | public function getPartialParserOutput() { |
59 | 60 | $this->process(); |
60 | 61 | |
61 | | - if( $this->changedNode != $this->root ) { |
| 62 | + if( isset( $this->previous ) && $this->changedNode != $this->root ) { |
62 | 63 | $markedWiki = $this->changedNode->render(); |
63 | 64 | if( wfRunHooks( 'InlineEditorPartialBeforeParse', array( &$markedWiki ) ) ) { |
64 | 65 | $output = $this->parse( $markedWiki ); |
— | — | @@ -67,7 +68,7 @@ |
68 | 69 | } |
69 | 70 | } |
70 | 71 | |
71 | | - return array( 'id' => $this->root->getId(), 'html' => $this->parse( $this->root->render() )->getText() ); |
| 72 | + return array( 'id' => $this->root->getId(), 'html' => $this->parseRoot()->getText() ); |
72 | 73 | } |
73 | 74 | |
74 | 75 | /** |
— | — | @@ -80,7 +81,7 @@ |
81 | 82 | */ |
82 | 83 | public function getFullParserOutput() { |
83 | 84 | $this->process(); |
84 | | - return $this->parse( $this->root->render() ); |
| 85 | + return $this->parseRoot(); |
85 | 86 | } |
86 | 87 | |
87 | 88 | /** |
— | — | @@ -90,10 +91,13 @@ |
91 | 92 | public function getTexts() { |
92 | 93 | $this->process(); |
93 | 94 | |
94 | | - $texts = array(); |
95 | | - foreach( $this->markings as $marking ) { |
96 | | - $texts[$marking->getId()] = substr( $this->wikiOriginal, $marking->getStart(), $marking->getLength() ); |
| 95 | + foreach( $this->markings as $id => $marking ) { |
| 96 | + $texts[$id] = substr( $this->wikiOriginal, $marking->getStart(), $marking->getLength() ); |
| 97 | + |
| 98 | + // force an empty string, as substr returns 'false' |
| 99 | + if( !$texts[$id] ) $texts[$id] = ''; |
97 | 100 | } |
| 101 | + |
98 | 102 | return $texts; |
99 | 103 | } |
100 | 104 | |
— | — | @@ -129,7 +133,7 @@ |
130 | 134 | if( $offset != 0) { |
131 | 135 | foreach( $this->markings as $id => $marking ) { |
132 | 136 | // if the marking is strictly after the edited marking, shift both start and end positions |
133 | | - if( $marking->getStart() >= $end ) { |
| 137 | + if( $marking->getStart() > $end ) { |
134 | 138 | $marking->setStart( $marking->getStart() + $offset ); |
135 | 139 | $marking->setEnd( $marking->getEnd() + $offset ); |
136 | 140 | } |
— | — | @@ -163,6 +167,16 @@ |
164 | 168 | } |
165 | 169 | |
166 | 170 | /** |
| 171 | + * Give special treatment to parsing the root. Add the root divs only after parsing |
| 172 | + * to make sure they survive the parsing. |
| 173 | + */ |
| 174 | + protected function parseRoot() { |
| 175 | + $output = $this->parse( $this->root->renderInside() ); |
| 176 | + $output->setText( $this->root->renderStartTag() . $output->getText() . $this->root->renderEndTag() ); |
| 177 | + return $output; |
| 178 | + } |
| 179 | + |
| 180 | + /** |
167 | 181 | * Have the wikitext marked by different extensions by calling the 'InlineEditorMark' hook. |
168 | 182 | * After that, tries to match previous markings against the new markings, and tries to preserve |
169 | 183 | * the previous markings, while growing $this->editedPiece if needed. |
— | — | @@ -176,7 +190,7 @@ |
177 | 191 | wfRunHooks( 'InlineEditorMark', array( &$this ) ); |
178 | 192 | |
179 | 193 | // sort the markings while preserving the keys (ids) |
180 | | - uasort( $this->markings, 'InlineEditorText::sortByStartAndLength' ); |
| 194 | + uasort( $this->markings, 'InlineEditorText::sortByStartLengthLevel' ); |
181 | 195 | |
182 | 196 | // collapse markings |
183 | 197 | $this->collapseMarkings(); |
— | — | @@ -193,7 +207,14 @@ |
194 | 208 | if( isset( $this->root ) ) return; |
195 | 209 | |
196 | 210 | $this->mark(); |
197 | | - $this->root = $this->buildTree( $this->markings ); |
| 211 | + |
| 212 | + // add the root marking to the list after building the tree, |
| 213 | + // so it will get in the list of markings, but isn't duplicated in the |
| 214 | + // tree |
| 215 | + $rootMarking = new InlineEditorRoot( $this->wikiOriginal ); |
| 216 | + $this->root = $this->buildTree( $this->markings, $rootMarking ); |
| 217 | + $this->markings[$rootMarking->getId()] = $rootMarking; |
| 218 | + |
198 | 219 | $this->changedNode = $this->findChangedNode(); |
199 | 220 | $this->applyLastEditHighlight(); |
200 | 221 | } |
— | — | @@ -249,11 +270,14 @@ |
250 | 271 | */ |
251 | 272 | protected function matchPreviousMarkings() { |
252 | 273 | // abort if there is nothing to match |
253 | | - if( empty( $this->previous ) ) return; |
| 274 | + if( !isset( $this->previous ) ) return; |
254 | 275 | |
| 276 | + // don't use the root of the previous markings |
| 277 | + unset( $this->previous['inline-editor-root'] ); |
| 278 | + |
255 | 279 | // sort the previous markings, while *re-keying* to natural numbers (0, 1, 2, ...) |
256 | 280 | // this is necessary to be able to run through the array using an integer pointer |
257 | | - usort( $this->previous, 'InlineEditorText::sortByStartAndLength' ); |
| 281 | + usort( $this->previous, 'InlineEditorText::sortByStartLengthLevel' ); |
258 | 282 | |
259 | 283 | // point to the start of the previous markings list |
260 | 284 | $indexPrevious = 0; |
— | — | @@ -267,7 +291,7 @@ |
268 | 292 | while( isset( $this->previous[$indexPrevious] ) ) { |
269 | 293 | $previous = $this->previous[$indexPrevious]; |
270 | 294 | |
271 | | - switch( self::sortByStartAndLength( $previous, $marking ) ) { |
| 295 | + switch( self::sortByStartLengthLevel( $previous, $marking ) ) { |
272 | 296 | case 1: |
273 | 297 | // if we've moved past the current marking, break, mismatch, and go to the next current marking |
274 | 298 | break(2); |
— | — | @@ -304,11 +328,12 @@ |
305 | 329 | * Build a tree from an array of sorted (!) markings. |
306 | 330 | * |
307 | 331 | * @param $markingsSorted array A sorted array of InlineEditorMarking objects. |
308 | | - * @return InlineEditorRoot |
| 332 | + * @param $rootMarking InlineEditorRoot A root marking, not included in the list of sorted markings |
| 333 | + * @return InlineEditorNode |
309 | 334 | */ |
310 | | - protected function buildTree( array $markingsSorted ) { |
| 335 | + protected function buildTree( array $markingsSorted, InlineEditorRoot $rootMarking ) { |
311 | 336 | // create the root |
312 | | - $root = new InlineEditorRoot( $this->wikiOriginal ); |
| 337 | + $root = new InlineEditorNode( $this->wikiOriginal, $rootMarking ); |
313 | 338 | |
314 | 339 | // $workingNode is the node we're trying to add children to |
315 | 340 | // initialise it to the root node |
— | — | @@ -373,10 +398,7 @@ |
374 | 399 | // find the markings contained in $this->editedPiece and mark them |
375 | 400 | $children = $this->root->findBestChildren( $this->editedPiece ); |
376 | 401 | foreach( $children as $child ) { |
377 | | - // don't mark if somehow root appears, as it has no marking attached |
378 | | - if( $child != $this->root ) { |
379 | | - $child->getMarking()->addClasses( 'lastEdit edited' ); |
380 | | - } |
| 402 | + $child->getMarking()->addClasses( 'lastEdit edited' ); |
381 | 403 | } |
382 | 404 | } |
383 | 405 | |
— | — | @@ -389,7 +411,7 @@ |
390 | 412 | * @param $b InlineEditorMarking |
391 | 413 | * @return int |
392 | 414 | */ |
393 | | - private static function sortByStartAndLength( $a, $b ) { |
| 415 | + private static function sortByStartLengthLevel( $a, $b ) { |
394 | 416 | if( $a->getStart() == $b->getStart() ) { |
395 | 417 | if( $a->getLength() == $b->getLength() ) { |
396 | 418 | if( $a->getLevel() == $b->getLevel() ) { |
Index: trunk/extensions/InlineEditor/InlineEditorNode.class.php |
— | — | @@ -1,18 +1,26 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | 4 | * This class wraps an InlineEditorMarking to be a part of a tree spanning the |
5 | | - * wikitext. Inherits from InlineEditorRoot, where most of the logic is. |
| 5 | + * wikitext. It is closely connected to the wikitext, and should be recreated whenever a |
| 6 | + * marking or wikitext changes. |
6 | 7 | */ |
7 | | -class InlineEditorNode extends InlineEditorRoot { |
| 8 | +class InlineEditorNode extends InlineEditorPiece { |
| 9 | + protected $wiki; /// < reference to the original wikitext |
| 10 | + protected $children; /// < array of children (InlineEditorNode) |
| 11 | + protected $isSorted; /// < bool whether or not the children are sorted |
| 12 | + protected $lastEnd; /// < largest endposition of children, to verify during adding the children are sortd |
8 | 13 | protected $marking; /// < marking this nodes wraps |
9 | | - protected $parent; /// < parent node, either an InlineEditorNode or InlineEditorRoot |
| 14 | + protected $parent; /// < parent node (InlineEditorNode) |
10 | 15 | |
11 | 16 | /** |
12 | 17 | * @param $wiki String Reference to the original wikitext |
13 | 18 | * @param $marking InlineEditorMarking Marking to wrap in the tree |
14 | 19 | */ |
15 | 20 | public function __construct( &$wiki, InlineEditorMarking $marking ) { |
16 | | - parent::__construct( $wiki ); |
| 21 | + $this->wiki =& $wiki; |
| 22 | + $this->children = array(); |
| 23 | + $this->lastEnd = 0; |
| 24 | + $this->isSorted = true; |
17 | 25 | $this->marking = $marking; |
18 | 26 | } |
19 | 27 | |
— | — | @@ -41,6 +49,14 @@ |
42 | 50 | } |
43 | 51 | |
44 | 52 | /** |
| 53 | + * Get an array of children of type InlineEditorNode. |
| 54 | + * @return array |
| 55 | + */ |
| 56 | + public function getChildren() { |
| 57 | + return $this->children; |
| 58 | + } |
| 59 | + |
| 60 | + /** |
45 | 61 | * Get the corresponding marking. |
46 | 62 | * @return InlineEditorMarking |
47 | 63 | */ |
— | — | @@ -49,8 +65,8 @@ |
50 | 66 | } |
51 | 67 | |
52 | 68 | /** |
53 | | - * Get the parent node or root. |
54 | | - * @return InlineEditorPiece |
| 69 | + * Get the parent node. |
| 70 | + * @return InlineEditorNode |
55 | 71 | */ |
56 | 72 | public function getParent() { |
57 | 73 | return $this->parent; |
— | — | @@ -71,4 +87,136 @@ |
72 | 88 | public function renderEndTag() { |
73 | 89 | return $this->marking->renderEndTag(); |
74 | 90 | } |
| 91 | + |
| 92 | + /** |
| 93 | + * Add a node to the list of children. |
| 94 | + * |
| 95 | + * Checks whether or not the child can be added. Returns false if it cannot add the |
| 96 | + * child, and true when it can. The calling class is responsible to add the node to |
| 97 | + * the innermost node, this will not be done by the function. |
| 98 | + * |
| 99 | + * It is recommended to add nodes from left to right, as this gives the best performance. |
| 100 | + * |
| 101 | + * @param $child InlineEditorNode |
| 102 | + * @return bool |
| 103 | + */ |
| 104 | + public function addChild( InlineEditorNode $child ) { |
| 105 | + // if we cannot contain the child, we cannot add it |
| 106 | + if( !$this->canContain( $child ) ) return false; |
| 107 | + |
| 108 | + // if the start is before the largest endpoint, check all children for overlap |
| 109 | + if( $child->getStart() < $this->lastEnd) { |
| 110 | + foreach( $this->children as $otherChild ) { |
| 111 | + if( $child->hasOverlap( $otherChild ) ) return false; |
| 112 | + } |
| 113 | + |
| 114 | + // if there is no overlap, we're sure that the list isn't sorted anymore |
| 115 | + $this->isSorted = false; |
| 116 | + } |
| 117 | + |
| 118 | + // add the child and set the parent of the child to $this |
| 119 | + $this->children[$child->getStart()] = $child; |
| 120 | + $child->parent = $this; |
| 121 | + |
| 122 | + // move $this->lastEnd if needed |
| 123 | + if( $child->getEnd() > $this->lastEnd ) $this->lastEnd = $child->getEnd(); |
| 124 | + |
| 125 | + return true; |
| 126 | + } |
| 127 | + |
| 128 | + /** |
| 129 | + * Find the node with the smallest length still able to contain $piece. |
| 130 | + * @param $piece InlineEditorPiece |
| 131 | + * @return InlineEditorNode |
| 132 | + */ |
| 133 | + public function findBestParent( InlineEditorPiece $piece ) { |
| 134 | + // if we cannot contain the piece, return false |
| 135 | + if( !$this->canContain( $piece ) ) return false; |
| 136 | + |
| 137 | + // sorted children is a precondition for the algoritm |
| 138 | + $this->sort(); |
| 139 | + |
| 140 | + foreach( $this->children as $start => $child ) { |
| 141 | + // if we've move past the end of the piece, stop |
| 142 | + if( $piece->getEnd() < $start ) break; |
| 143 | + |
| 144 | + // try to fit the piece to this child |
| 145 | + if( $piece->getStart() >= $start ) { |
| 146 | + $fit = $child->findBestParent( $piece ); |
| 147 | + // if we found a child that fits the piece, return it |
| 148 | + if( $fit !== false ) { |
| 149 | + return $fit; |
| 150 | + } |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + // if we cannot find a suitable child, but we can contain it in this piece, return $this |
| 155 | + return $this; |
| 156 | + } |
| 157 | + |
| 158 | + /** |
| 159 | + * Find the highest level of children that can be fit into a certain piece. |
| 160 | + * This will return an array of nodes that are best fit. |
| 161 | + * @param $piece InlineEditorPiece |
| 162 | + * @return array |
| 163 | + */ |
| 164 | + public function findBestChildren( InlineEditorPiece $piece ) { |
| 165 | + // try to find a parent that fits $piece (which can very well be $this!) |
| 166 | + $parent = $this->findBestParent( $piece ); |
| 167 | + |
| 168 | + // if we cannot find a parent, return false |
| 169 | + if( !$parent ) return false; |
| 170 | + |
| 171 | + // if the piece can contain the entire parent piece, just return that piece |
| 172 | + if( $piece->canContain( $parent ) ) return array( $parent ); |
| 173 | + |
| 174 | + // sorting is a precondition of the algoritm |
| 175 | + $this->sort(); |
| 176 | + |
| 177 | + $children = array(); |
| 178 | + foreach( $parent->children as $start => $child ) { |
| 179 | + // if we've moved past the end of the piece, stop |
| 180 | + if( $start > $piece->getEnd() ) break; |
| 181 | + |
| 182 | + // add the child to the list if it can be contained in the piece |
| 183 | + if( $piece->canContain( $child ) ) { |
| 184 | + $children[] = $child; |
| 185 | + } |
| 186 | + } |
| 187 | + return $children; |
| 188 | + } |
| 189 | + |
| 190 | + /** |
| 191 | + * Render the entire tag, with recursion on the children. |
| 192 | + * @return string HTML |
| 193 | + */ |
| 194 | + public function render() { |
| 195 | + return $this->renderStartTag() . $this->renderInside() . $this->renderEndTag(); |
| 196 | + } |
| 197 | + |
| 198 | + /** |
| 199 | + * Render the inside of the tag. |
| 200 | + */ |
| 201 | + public function renderInside() { |
| 202 | + $this->sort(); |
| 203 | + $lastPos = $this->getStart(); |
| 204 | + $output = ''; |
| 205 | + foreach( $this->children as $child ) { |
| 206 | + $output .= substr( $this->wiki, $lastPos, $child->getStart() - $lastPos ); |
| 207 | + $output .= $child->render(); |
| 208 | + $lastPos = $child->getEnd(); |
| 209 | + } |
| 210 | + |
| 211 | + $output .= substr( $this->wiki, $lastPos, $this->getEnd() - $lastPos ); |
| 212 | + return $output; |
| 213 | + } |
| 214 | + |
| 215 | + /** |
| 216 | + * Sort the children by start position (key). |
| 217 | + */ |
| 218 | + protected function sort() { |
| 219 | + if( $this->isSorted ) return; |
| 220 | + ksort( $this->children ); |
| 221 | + $this->isSorted = true; |
| 222 | + } |
75 | 223 | } |
\ No newline at end of file |
Index: trunk/extensions/InlineEditor/jquery.inlineEditor.api.js |
— | — | @@ -0,0 +1,22 @@ |
| 2 | +/** |
| 3 | + * Useful calls for other plugins (extensions, browser apps, etc.) |
| 4 | + */ |
| 5 | +( function( $ ) { $.inlineEditor.api = { |
| 6 | + getFullText: function() { |
| 7 | + return $.inlineEditor.getTextById( 'inline-editor-root' ); |
| 8 | + }, |
| 9 | + |
| 10 | + previewFullText: function( text ) { |
| 11 | + $.inlineEditor.cancel(); |
| 12 | + $.inlineEditor.previewTextById( text, 'inline-editor-root' ); |
| 13 | + }, |
| 14 | + |
| 15 | + openFullEditor: function() { |
| 16 | + $.inlineEditor.show( 'inline-editor-root' ); |
| 17 | + }, |
| 18 | + |
| 19 | + getFullEditorTextarea: function() { |
| 20 | + $.inlineEditor.api.openFullEditor(); |
| 21 | + return $( '#inline-editor-root textarea' ); |
| 22 | + } |
| 23 | +}; } ) ( jQuery ); |
Property changes on: trunk/extensions/InlineEditor/jquery.inlineEditor.api.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 24 | + native |
Index: trunk/extensions/InlineEditor/InlineEditorMarking.class.php |
— | — | @@ -41,8 +41,7 @@ |
42 | 42 | $this->id = self::uniqueId(); |
43 | 43 | |
44 | 44 | $this->classes = array(); |
45 | | - $this->addClasses( $classes ); |
46 | | - |
| 45 | + $this->addClasses( $classes ); |
47 | 46 | } |
48 | 47 | |
49 | 48 | /** |
Index: trunk/extensions/InlineEditor/InlineEditor.i18n.php |
— | — | @@ -14,7 +14,8 @@ |
15 | 15 | $messages['en'] = array( |
16 | 16 | 'inline-editor-desc' => 'Provides an alternative editor which is easier to use', |
17 | 17 | |
18 | | - 'inline-editor-editbox-top' => "'''Awesome, you are editing {{SITENAME}}!'''<br />You can edit the article below, by clicking on <span class=\"highlightExample\">blue elements</span> in the page.", |
| 18 | + 'inline-editor-editbox-top' => "'''Awesome, you are editing {{SITENAME}}!'''<br />You can edit the page below, by clicking on <span class=\"highlightExample\">blue elements</span> in the page.", |
| 19 | + 'inline-editor-editbox-top-new' => "'''Cool, you are creating a new page on {{SITENAME}}!'''<br />You can start typing in the <span class=\"highlightExample\">textbox</span> below.", |
19 | 20 | 'inline-editor-editbox-changes-question' => 'Can you briefly describe the changes you are making?', |
20 | 21 | 'inline-editor-editbox-changes-example' => 'For example: "Fixed spelling mistake", "Corrected facts", "Wrote a new paragraph", etc.', |
21 | 22 | 'inline-editor-editbox-publish-notice' => "When you are done, do not forget to publish the page!", |
— | — | @@ -37,6 +38,7 @@ |
38 | 39 | */ |
39 | 40 | $messages['qqq'] = array( |
40 | 41 | 'inline-editor-editbox-top' => 'The "edit box" should be as small as possible. It should present the most essential information, and nothing more. It should ask for nothing more but the bare minimum. I chose to include a few basic guidelines, starting with some positive reinforcement: "Awesome, you\'re editing Wikipedia!". This invites novice users to actually edit the article. After all, what they are doing is "awesome"!', |
| 42 | + 'inline-editor-editbox-top-new' => 'The welcoming message when creating a new page is similar, but slightly different than the usual message. First, you can use another encouraging word, in this case "Cool", and then tell the user to start typing in the textbox. The word textbox is given a blue highlight, as the textbox below also looks blue on the borders.', |
41 | 43 | 'inline-editor-editbox-changes-question' => "The line above the edit summary is chosen very carefully: \"Can you briefly describe the changes you're making?\" |
42 | 44 | Asking for \"changes you ''have'' made\" looks strange when first encountering this page. |
43 | 45 | Asking for \"changes you ''will be'' making\" looks strange when |
— | — | @@ -63,4 +65,3 @@ |
64 | 66 | unambiguous: it will be shown to the world. On the other hand, using the word "Publish" may have legal |
65 | 67 | consequences in some countries, which should be looked into.', |
66 | 68 | ); |
67 | | - |
Index: trunk/extensions/InlineEditor/jquery.inlineEditor.css |
— | — | @@ -72,111 +72,10 @@ |
73 | 73 | -webkit-border-radius: 10px; |
74 | 74 | } |
75 | 75 | |
76 | | -#siteNotice div.editmode { |
77 | | - /* style the mode selection box essentially in the same way as the box above */ |
78 | | - border: 1px solid #A7D7F9; |
79 | | - padding: 0px; |
80 | | - max-width: 720px; |
| 76 | +#siteNotice div.introbox { |
| 77 | + background-color: #f3f3f3; |
| 78 | + border: 1px solid #aaa; |
81 | 79 | margin-bottom: 10px; |
82 | | -} |
83 | | - |
84 | | -#siteNotice div.editmode .header { |
85 | | - /* only the header has a blue background color */ |
86 | | - background-color: #eaf3f8; |
87 | | - margin: 0px; |
88 | | - padding: 0px; |
89 | | - |
90 | | - /* make sure the header resizes due to the floated elements */ |
91 | | - overflow: auto; |
92 | | - |
93 | | - /* make the bottom borders of the buttons collapse */ |
94 | | - margin-bottom: -1px; |
95 | | -} |
96 | | - |
97 | | -#siteNotice div.editmode .header .radio, #siteNotice div.editmode .header .button { |
98 | | - /* margin fix for old IE versions, see http://www.positioniseverything.net/explorer/doubled-margin.html */ |
99 | | - display: inline; |
100 | | - float: left; |
101 | | - |
102 | | - /* have the borders here, not in the header div, to make sure it looks good on smaller displays! */ |
103 | | - border-top: 1px solid #A7D7F9; |
104 | | - border-right: 1px solid #A7D7F9; |
105 | | - border-bottom: 1px solid #A7D7F9; |
106 | | - padding: 2px 6px; |
107 | | - height: 22px; |
108 | | - |
109 | | - /* collapse the top border */ |
110 | | - margin-top: -1px; |
111 | | -} |
112 | | - |
113 | | -#siteNotice div.editmode .header .button { |
114 | | - /* undo and redo buttons on the right, as well as the counter */ |
115 | | - float: right; |
116 | | - border-left: 1px solid #A7D7F9; |
117 | | - border-right: none; |
118 | | -} |
119 | | - |
120 | | -#siteNotice div.editmode .header .button a { |
121 | | - font-size: 0.9em; |
122 | | -} |
123 | | - |
124 | | -/*#siteNotice div.editmode .header .button */ #editCounter { |
125 | | - font-size: 0.9em; |
126 | | - color: #555; |
127 | | -} |
128 | | - |
129 | | -/*#siteNotice div.editmode .header .button */ #editCounter.changeHighlight { |
130 | | - color: #eaf3f8; |
131 | | -} |
132 | | - |
133 | | -#siteNotice div.editmode .header .radio input { |
134 | | - /* use only the padding of .radio div, and the label margin */ |
135 | | - margin: 0px; |
136 | | -} |
137 | | - |
138 | | -#siteNotice div.editmode .header .title { |
139 | | - /* bold title on the left side of the header */ |
140 | | - font-size: 0.9em; |
141 | | - font-weight: bold; |
142 | | -} |
143 | | - |
144 | | -#siteNotice div.editmode .header .radio label { |
145 | | - /* only have a small margin on the left side */ |
146 | | - font-size: 0.9em; |
147 | | - margin: 0px 0px 0px 5px; |
148 | | - |
149 | | - /* create a hand cursor, cross-browser hack: http://www.quirksmode.org/css/cursor.html */ |
150 | | - cursor: pointer; |
151 | | - cursor: hand; |
152 | | -} |
153 | | - |
154 | | -#siteNotice div.editmode .header .radio label:hover { |
155 | | - /* make the labels recognizable as clickable */ |
156 | | - text-decoration: underline; |
157 | | -} |
158 | | - |
159 | | -#siteNotice div.editmode .descriptionOuter { |
160 | | - /* have a top border which collapses with the label borders */ |
161 | | - border-top: 1px solid #A7D7F9; |
162 | | - |
163 | | - /* use padding and margin only in the inner div */ |
164 | | - padding: 0px; |
165 | | - margin: 0px; |
166 | | - |
167 | | - |
168 | | - /* make sure the box works correctly for the nice animation */ |
169 | | - background-color: #f8f8f8; |
| 80 | + padding: 10px 10px 5px; |
170 | 81 | position: relative; |
171 | | - overflow: hidden; |
172 | 82 | } |
173 | | - |
174 | | -#siteNotice div.editmode .descriptionInner { |
175 | | - /* have the same padding as the box above */ |
176 | | - padding: 10px 10px 5px; |
177 | | - margin: 0px; |
178 | | - |
179 | | - /* position all the inner descriptions at the same place for the nice animation */ |
180 | | - position: absolute; |
181 | | - top: 0px; |
182 | | - left: 0px; |
183 | | -} |
Index: trunk/extensions/InlineEditor/ExtendedEditPage.class.php |
— | — | @@ -10,7 +10,7 @@ |
11 | 11 | * Inits the edit page for the InlineEditor. |
12 | 12 | * This is largely a copy-paste from EditPage::edit(), with some specific changes. |
13 | 13 | */ |
14 | | - public function initInlineEditor() { |
| 14 | + public function initInlineEditor( $inlineEditor ) { |
15 | 15 | global $wgRequest, $wgOut; |
16 | 16 | $this->importFormData( $wgRequest ); |
17 | 17 | |
— | — | @@ -65,10 +65,8 @@ |
66 | 66 | $wgOut->clearHTML(); |
67 | 67 | if ( $this->formtype == 'initial' || $this->firsttime ) { |
68 | 68 | $this->showIntro(); |
69 | | - if ( $wgOut->getHTML() != '' ) { |
70 | | - $wgOut->clearHTML(); |
71 | | - return false; |
72 | | - } |
| 69 | + $inlineEditor->setIntro( $wgOut->getHTML() ); |
| 70 | + $wgOut->clearHTML(); |
73 | 71 | } |
74 | 72 | |
75 | 73 | if ( 'initial' == $this->formtype || 'preview' == $this->formtype || $this->firsttime ) { |
Index: trunk/extensions/InlineEditor/jquery.inlineEditor.editors.basic.js |
— | — | @@ -66,7 +66,7 @@ |
67 | 67 | }, |
68 | 68 | |
69 | 69 | /** |
70 | | - * Default click handler for simple editors. Recommended to override. |
| 70 | + * Default click handler for simple editors. |
71 | 71 | */ |
72 | 72 | click: function( event ) { |
73 | 73 | var $field = $(this); |
— | — | @@ -75,31 +75,45 @@ |
76 | 76 | // prevent clicks from reaching other elements |
77 | 77 | event.stopPropagation(); |
78 | 78 | event.preventDefault(); |
79 | | - |
| 79 | + |
80 | 80 | // disable the existing editing field if necessary |
81 | 81 | $.inlineEditor.editors.basic.cancel(); |
82 | 82 | |
83 | | - // find the element and retrieve the corresponding wikitext |
84 | | - var wiki = $.inlineEditor.getTextById( $field.attr( 'id' ) ); |
| 83 | + $.inlineEditor.editors.basic.show( $field.attr( 'id' ) ); |
| 84 | + } |
| 85 | + }, |
| 86 | + |
| 87 | + /** |
| 88 | + * Actually handles showing the editing interface. Recommended to override. |
| 89 | + * @return Boolean Whether or not showing the interface was successful. |
| 90 | + */ |
| 91 | + show: function( id ) { |
| 92 | + $field = $( '#' + id ); |
| 93 | + |
| 94 | + // if the class is incorrect, terminate |
| 95 | + if( !$field.hasClass( 'inlineEditorBasic' ) ) return false; |
| 96 | + |
| 97 | + // find the element and retrieve the corresponding wikitext |
| 98 | + var wiki = $.inlineEditor.getTextById( $field.attr( 'id' ) ); |
| 99 | + |
| 100 | + // create the edit field and build the edit bar |
| 101 | + var $newField = $.inlineEditor.editors.basic.newField( $field, $.inlineEditor.editors.basic.click ); |
| 102 | + $.inlineEditor.editors.basic.addEditBar( $newField, wiki ); |
| 103 | + |
| 104 | + // add the wikiEditor toolbar |
| 105 | + if( $.fn.wikiEditor ) { |
| 106 | + $textarea = $newField.find( 'textarea' ); |
85 | 107 | |
86 | | - // create the edit field and build the edit bar |
87 | | - var $newField = $.inlineEditor.editors.basic.newField( $field, $.inlineEditor.editors.basic.click ); |
88 | | - $.inlineEditor.editors.basic.addEditBar( $newField, wiki ); |
| 108 | + if( $.wikiEditor.modules.toolbar && $.wikiEditor.modules.toolbar.config && $.wikiEditor.isSupported( $.wikiEditor.modules.toolbar ) ) { |
| 109 | + $textarea.wikiEditor( 'addModule', $.wikiEditor.modules.toolbar.config.getDefaultConfig() ); |
| 110 | + } |
89 | 111 | |
90 | | - // add the wikiEditor toolbar |
91 | | - if( $.fn.wikiEditor ) { |
92 | | - $textarea = $newField.find( 'textarea' ); |
93 | | - |
94 | | - if( $.wikiEditor.modules.toolbar && $.wikiEditor.modules.toolbar.config && $.wikiEditor.isSupported( $.wikiEditor.modules.toolbar ) ) { |
95 | | - $textarea.wikiEditor( 'addModule', $.wikiEditor.modules.toolbar.config.getDefaultConfig() ); |
96 | | - } |
97 | | - |
98 | | - if( $.wikiEditor.modules.dialogs && $.wikiEditor.modules.dialogs.config && $.wikiEditor.isSupported( $.wikiEditor.modules.dialogs ) ) { |
99 | | - $.wikiEditor.modules.dialogs.config.replaceIcons( $textarea ); |
100 | | - $textarea.wikiEditor( 'addModule', $.wikiEditor.modules.dialogs.config.getDefaultConfig() ); |
101 | | - } |
| 112 | + if( $.wikiEditor.modules.dialogs && $.wikiEditor.modules.dialogs.config && $.wikiEditor.isSupported( $.wikiEditor.modules.dialogs ) ) { |
| 113 | + $.wikiEditor.modules.dialogs.config.replaceIcons( $textarea ); |
| 114 | + $textarea.wikiEditor( 'addModule', $.wikiEditor.modules.dialogs.config.getDefaultConfig() ); |
102 | 115 | } |
103 | 116 | } |
| 117 | + return true; |
104 | 118 | }, |
105 | 119 | |
106 | 120 | /** |
Index: trunk/extensions/InlineEditor/InlineEditorRecommended.php |
— | — | @@ -23,4 +23,3 @@ |
24 | 24 | require_once( dirname(__FILE__) . "/TemplateEditor/TemplateEditor.php" ); |
25 | 25 | require_once( dirname(__FILE__) . "/ParagraphEditor/ParagraphEditor.php" ); |
26 | 26 | require_once( dirname(__FILE__) . "/SectionEditor/SectionEditor.php" ); |
27 | | -require_once( dirname(__FILE__) . "/FullEditor/FullEditor.php" ); |
Index: trunk/extensions/InlineEditor/jquery.inlineEditor.js |
— | — | @@ -159,6 +159,21 @@ |
160 | 160 | }, |
161 | 161 | |
162 | 162 | /** |
| 163 | + * Show the interface for a particular element. |
| 164 | + * @return Boolean Whether or not showing the interface was successful. |
| 165 | + */ |
| 166 | + show: function( id ) { |
| 167 | + // disable the existing editing field if necessary |
| 168 | + $.inlineEditor.editors.basic.cancel(); |
| 169 | + |
| 170 | + // try the show function of all editors |
| 171 | + for( var optionNr in $.inlineEditor.editors ) { |
| 172 | + if( $.inlineEditor.editors[optionNr].show( id ) ) return true; |
| 173 | + } |
| 174 | + return false; |
| 175 | + }, |
| 176 | + |
| 177 | + /** |
163 | 178 | * Submit event, adds the json to the hidden field |
164 | 179 | */ |
165 | 180 | submit: function( event ) { |