1 /* 2 --- 3 4 description: A content assist for textares of your webpage. 5 6 license: GNU General Public License, version 2. 7 8 authors: 9 - Andrea Dessì <nkjoep@gmail.com> 10 11 requires: 12 core/1.3: 13 - all 14 more/1.3: 15 - all (sorry I should investigate about which More classes are needed.) 16 17 provides: [MooContentAssist] 18 19 ... 20 */ 21 /* 22 Changelog: 23 14 Mar 2011 v0.80.3 - namespace parser, fixed "charAt()" problem with IE7 24 08 Mar 2011 v0.80.2 - namespace parser, now with allowed chars (or strings) in the namespace 25 08 Mar 2011 v0.80.1 - configurable items container inside the main box 26 06 Mar 2011 v0.80 - MooTools 1.3, several bugfixing, internal API rewritten. 27 01 Jul 2010 v0.70.4 - converter from xml to words object, fixed bug on foundlist, fixed bug on assist window position 28 27 Jun 2010 v0.70 - theme changer, new demo with theme toggler 29 11 Jun 2010 v0.70 - configurable number of item shown in the box 30 10 Jun 2010 v0.70 - scrollable result box, scrollable result box shows always the current item in the middle 31 04 Jun 2010 v0.68 - few standard methods for positioning, css rules methods 32 24 May 2010 v0.68 - fixed textarea scroll when inserting keywords, fixed assistWindow position 33 23 May 2010 v0.66 - first dot fixed, occurence text highlight fixed, animation now is a parameter 34 22 May 2010 v0.64 - ie7 fixes 35 21 May 2010 v0.63 - added events "click" and "over" to the shown items, when showing assistWindow first item is already selected, added "." trigger 36 21 May 2010 v0.60 - added styles for items, window positioning 37 20 May 2010 v0.55 - fixed textarea events 38 16 May 2010 v0.25 - fixed words data structure 39 15 May 2010 v0.15 - added completed text, events and keys 40 13 May 2010 v0.0 - hello word 41 */ 42 /* JSHint globals 43 globals Events: false, Options: false, MooTools: false, Class: false, Element: false, typeOf: false, instanceOf: false, Fx: false, Slick: false, Type: false, Chain: false, Elements: false, Document: false, Event: false, Window: false, Browser: false , Request: false, Keyboard: false */ 44 45 /** 46 * @author Andrea Dessì <nkjoep@gmail.com> 47 * @fileoverview A content assist for textares of your webpage. {@link https://github.com/NKjoep/MooContentAssist} 48 * @version 0.80.3 49 * 50 * */ 51 /** 52 Construct a new MooContentAssist object. 53 @class {MooContentAssist} This is the basic MooContentAssist class. It adds a content/code assist functionality to your textareas. 54 @constructor 55 @param {Object} options 56 @param {HtmlElement} options.source The html element of an input or textarea. 57 @param {Integer} [options.frameSize=3] How many items show in the window at same size. 58 @param {Integer} [options.animationDuration=75] How long the show/hide animation in milliseconds 59 @param {Object} options.vocabulary The JSON obj representing the vocabulary 60 @param {Boolean} [options.vocabularyDiscoverer=true] Toggle the automatic words discoverer on/off 61 @param {String} [options.vocabularyUrl] The url for ajax calls. It's optional. 62 @param {String} [options.vocabularyUrlParam="ns"] The name of the querystring variable passed to the options.vocabularyUrl in ajax calls 63 @param {String} [options.vocabularyUrlMethod="get"] Supported: get, post. Used for ajax calls. 64 @param {Object} [options.windowPadding={x: 0, y: 2}] The margin of the assist window. It's an object with x,y keys. 65 @param {String} [options.itemType="li"] The tag used for generating itemsContainer 66 @param {String} [options.itemsContainer="ul"] The tag used for generating the items container 67 @param {String} [options.matchedTextItemType="span"] The tag used for the matched text 68 @param {Boolean} [options.aggressiveAssist="true"] Toggle aggressiveAssist mode on/offset 69 @param {String[]} [options.namespaceAllowed=["()", "$"]] Define which strings can be contained in namespaces item. 70 @param {Object} [options.css] 71 @param {String} [options.css.item="item"] The css class for the single item 72 @param {String} [options.css.itemsContainer="itemsContainer"] The css for the items container 73 @param {String} [options.css.itemSelected="itemSelected"] The css class added to the item when it's selected 74 @param {String} [options.css.messageItem="message"] 75 @param {String} [options.css.matchedText="matched"] 76 @param {Object} [options.labels] 77 @param {String} [options.labels.nothingFound="Nothing was found."] 78 @param {String} [options.labels.ajaxError="Error while retrieving data."] 79 @param {Function} [options.vocabularyManager_GetVocabulary] 80 @param {Function} [options.vocabularyManager_Extract] 81 @param {Function} [options.vocabularyManager_Render] 82 @return {MooContentAssist} A MooContentAssist 83 * 84 */ 85 var MooContentAssist = new Class({ 86 version: "MooContentAssist v0.80.3", 87 Implements: [Events, Options], 88 options: { 89 source: null, 90 frameSize: 3, 91 animationDuration: 75, 92 vocabulary: null, 93 vocabularyDiscoverer: true, 94 vocabularyUrl: null, 95 vocabularyUrlParam: "ns", 96 vocabularyUrlMethod: "get", 97 windowPadding: {x: 0, y: 2}, 98 itemType: "li", 99 itemsContainerType: "ul", 100 matchedTextItemType: "span", 101 aggressiveAssist: true, 102 namespaceAllowed: ["()", "$"], 103 css : { 104 item: "item", 105 itemsContainer: "itemsContainer", 106 itemSelected: "itemSelected", 107 messageItem: "message", 108 matchedText: "matched" 109 }, 110 labels: { 111 nothingFound: "Nothing was found.", 112 ajaxError: "Error while retrieving data." 113 }, 114 vocabularyManager_Render: function(obj) { 115 var ns = this.getNameSpace().getLast(); 116 var rendered = new Element(this.options.itemType,{"class": this.options.css.item}); 117 rendered.store("value",obj); 118 if (ns!="/") { 119 new Element(this.options.matchedTextItemType,{text: obj.substring(0,ns.length), "class": this.options.css.matchedText}).inject(rendered); 120 obj=obj.substring(ns.length); 121 } 122 rendered.appendText(obj); 123 return rendered; 124 }, 125 vocabularyManager_Extract: function(namespace,vocabulary) { 126 if (namespace[0] === "") { 127 namespace=Array.clone(namespace); 128 namespace.shift(); 129 } 130 var vocabularyFound = []; 131 var found = null; 132 var searchKey = null; 133 if (namespace.length === 1){ 134 found = vocabulary; 135 if (namespace[0] != "/") { 136 searchKey = namespace[0]; 137 searchKey = searchKey.replace(/\*/g,"\\\*"); 138 searchKey = searchKey.replace(/\./g,"\\\."); 139 searchKey = searchKey.replace(/\?/g,"\\\?"); 140 searchKey = searchKey.replace(/\[/g,"\\\["); 141 searchKey = searchKey.replace(/\]/g,"\\\]"); 142 searchKey = searchKey.replace(/\(/g,"\\\("); 143 searchKey = searchKey.replace(/\)/g,"\\\)"); 144 searchKey = searchKey.replace(/\{/g,"\\\{"); 145 searchKey = searchKey.replace(/\}/g,"\\\}"); 146 searchKey = searchKey.replace(/\^/g,"\\\^"); 147 searchKey = searchKey.replace(/\$/g,"\\\$"); 148 } 149 } 150 else if (namespace.length > 1) { 151 if (namespace[namespace.length-1] != "/") { 152 searchKey = namespace[namespace.length-1]; 153 searchKey = searchKey.replace(/\|/g,"\\\|"); 154 searchKey = searchKey.replace(/\*/g,"\\\*"); 155 searchKey = searchKey.replace(/\./g,"\\\."); 156 searchKey = searchKey.replace(/\?/g,"\\\?"); 157 searchKey = searchKey.replace(/\[/g,"\\\["); 158 searchKey = searchKey.replace(/\]/g,"\\\]"); 159 searchKey = searchKey.replace(/\(/g,"\\\("); 160 searchKey = searchKey.replace(/\)/g,"\\\)"); 161 searchKey = searchKey.replace(/\{/g,"\\\{"); 162 searchKey = searchKey.replace(/\}/g,"\\\}"); 163 searchKey = searchKey.replace(/\^/g,"\\\^"); 164 searchKey = searchKey.replace(/\$/g,"\\\$"); 165 } 166 namespace=Array.clone(namespace); 167 namespace.pop(); 168 var tempFound = vocabulary; 169 for (var i=0;i<namespace.length;i++) { 170 try { 171 tempFound = tempFound[namespace[i]]; 172 } 173 catch (e) { 174 tempFound = null; 175 } 176 } 177 found = tempFound; 178 } 179 if (null !== found) { 180 if(typeOf(found)=="object") { 181 Object.each(found,function(value,key){ 182 if (searchKey === null || key.test("^"+searchKey,"i")) { 183 vocabularyFound.push(key); 184 } 185 }); 186 } 187 else if(typeOf(found)=="array") { 188 Array.each(found,function(item,index,object) { 189 if(typeOf(item)=="string" || typeOf(item)=="number") { 190 item = item.toString(); 191 if (item.length>0) { 192 if (searchKey === null || item.test("^"+searchKey,"i")) { 193 vocabularyFound.push(item.toString()); 194 } 195 } 196 } 197 }); 198 } 199 vocabularyFound.sort(); 200 } 201 return vocabularyFound; 202 }, 203 vocabularyManager_GetVocabulary: function(namespace) { 204 var currentNamespace = namespace; 205 this._currentVocabulary = null; 206 var extractedVocabulary = null; 207 if (typeOf(this.options.vocabularyUrl)=="string") { 208 var namespaceData = this.options.vocabularyUrlParam+"="+currentNamespace.join("."); 209 if (this.vocabularyRequest===undefined) { 210 this.vocabularyRequest = new Request.JSON({ 211 secure: true, 212 url: this.options.vocabularyUrl, 213 method: this.options.vocabularyUrlMethod, 214 //data: namespaceData, 215 async: false, 216 link: "cancel", 217 onSuccess: function(obj) { 218 this.currentVocabulary = obj; 219 }, 220 onFailure: function(xhr) { 221 var messageEl = this._createMessage(this.options.labels.ajaxError); 222 this.setAssistWindowContent(messageEl); 223 }.bind(this) 224 }); 225 } 226 else { 227 this.vocabularyRequest.cancel(); 228 } 229 this.vocabularyRequest.currentVocabulary = null; 230 var reqObj = {}; 231 reqObj[this.options.vocabularyUrlParam] = currentNamespace; 232 this.vocabularyRequest.send(namespaceData); 233 extractedVocabulary = this.options.vocabularyManager_Extract.call(this,currentNamespace,this.vocabularyRequest.currentVocabulary); 234 } 235 else { 236 extractedVocabulary = this.options.vocabularyManager_Extract.call(this,currentNamespace,this.options.vocabulary); 237 } 238 return extractedVocabulary; 239 } 240 }, 241 _checkFocus: function(target) { 242 var t = target; 243 var s = this.options.source; 244 var assistWindow = this.getAssistWindow(); 245 var checkA = (assistWindow!==null) && (t == assistWindow || assistWindow.contains(t)); 246 var checkB = (t == s || s.contains(t)); 247 if (t==window) return false; 248 else if (checkA || checkB) return true; 249 else { 250 return false; 251 } 252 }, 253 _createMessage: function(text) { 254 var messageEl = new Element(this.options.itemType, { 255 "class": this.options.css.messageItem, 256 "text": text 257 }); 258 return messageEl; 259 }, 260 _discoverUserVocabulary: function(namespace) { 261 var found = []; 262 if (namespace.length==1) { 263 namespace = namespace[0]; 264 var that = this; 265 found = this._discoverWords(this.getSourceValue()); 266 found = found.filter(function(item, index){ 267 var check = false; 268 if (namespace == "/") { 269 check = true; 270 } 271 else if (namespace==item) { 272 check = false; 273 } 274 else if (item.substring(0,namespace.length).toLowerCase() == namespace.toLowerCase()) { 275 check = true; 276 } 277 return check; 278 }); 279 found.sort(); 280 } 281 return found; 282 }, 283 _discoverWords: function(str){ 284 str = str.replace(/\W/g," ").clean().split(" ").clean().unique(); 285 var tmp = []; 286 str.each(function(item, index){ 287 if (item.length > 3) { 288 tmp.push(item.clean()); 289 } 290 }); 291 str = tmp; 292 return str; 293 }, 294 _eventManager: function() { 295 this.addEvents({ 296 "start": function(mca) { this.start(); }.bind(this), 297 "end": function(mca) { this.end(); }.bind(this) 298 }); 299 var myKeyboardEvents = new Keyboard({ 300 active: false, 301 events: { 302 "alt+space": function(ev){ 303 if (this.getAssistWindow()!==null) { 304 //("already assisting!"); 305 ev.preventDefault(); 306 } 307 else { 308 //("start assist"); 309 ev.preventDefault(); 310 this.fireEvent("start",this); 311 } 312 }.bind(this), 313 "control+space": function(ev){ 314 //("already assisting!"); 315 if (this.getAssistWindow()!==null) { 316 ev.preventDefault(); 317 this.fireEvent("start",this); 318 } 319 else { 320 //("start assist"); 321 ev.preventDefault(); 322 this.fireEvent("start",this); 323 } 324 }.bind(this), 325 "up": function(ev) { 326 //("select item up"); 327 if (this.getAssistWindow()!==null) { 328 ev.preventDefault(); 329 this.selectItemUp(); 330 } 331 }.bind(this), 332 "down": function(ev) { 333 //("select item down"); 334 if (this.getAssistWindow()!==null) { 335 ev.preventDefault(); 336 this.selectItemDown(); 337 } 338 }.bind(this), 339 "esc": function(ev) { 340 //("close it!"); 341 if (this.getAssistWindow()!==null) { 342 ev.preventDefault(); 343 this.fireEvent("end",this); 344 } 345 }.bind(this), 346 "tab": function(ev) { 347 //("close it!"); 348 if (this.getAssistWindow()!==null) { 349 ev.preventDefault(); 350 this.fireEvent("end",this); 351 } 352 }.bind(this), 353 "enter": function(ev) { 354 //("use the item! and destroy it!"); 355 if(this.getAssistWindow()!==null && this.getItemSelected()!==null) { 356 ev.preventDefault(); 357 this._useItemSelected(); 358 this.fireEvent("end",this); 359 } 360 }.bind(this), 361 "keyup:delete": function(ev){ 362 if(this.getAssistWindow()!==null) { 363 this.fireEvent("start",this); 364 } 365 }.bind(this), 366 "keyup:cancel": function(ev){ 367 if (this.getAssistWindow()!==null) { 368 this.fireEvent("start",this); 369 } 370 }.bind(this), 371 "keyup:backspace": function(ev){ 372 if (this.getAssistWindow()!==null) { 373 this.fireEvent("start",this); 374 } 375 }.bind(this), 376 "keyup:space": function(ev){ 377 this.fireEvent("end",this); 378 }.bind(this) 379 } 380 }); 381 var that = this; 382 this.options.source.addEvents({ 383 "focus": function(ev) { 384 this[1]._setSourceCaretPosition(); 385 this[0].activate(); 386 }.bind([myKeyboardEvents,this]), 387 "blur": function(ev) { 388 this[0].deactivate(); 389 if (!this[1]._checkFocus(ev.target)) { 390 this[1].fireEvent("end",this[1]); 391 } 392 }.bind([myKeyboardEvents,this]), 393 "keyup": function(ev){ 394 this._setSourceCaretPosition(); 395 if (this.getAssistWindow()!==null||this.options.aggressiveAssist) { 396 //removed control, for strange behaviour when selecting all with control+a 397 if(!ev.control && ev.key.length == 1 && ev.key.test(/^\w$/)) { 398 this.fireEvent("start",this); 399 } 400 } 401 }.bind(this), 402 "keypress": that._setSourceCaretPosition.bind(this), 403 "keydown": that._setSourceCaretPosition.bind(this) 404 }); 405 this.options.source.set("autocomplete","off"); 406 this.options.source.setProperty("autocomplete","off"); 407 window.addEvent("click",function(ev) { 408 if (!this._checkFocus(ev.target)) { 409 this.fireEvent("end",this); 410 } 411 }.bind(this)); 412 }, 413 _mergeVocabulary: function(vocabulary, vocabularyToInclude) { 414 var merged = vocabulary.combine(vocabularyToInclude).sort(); 415 return merged; 416 }, 417 _namespaceParser: function(nameSpaceString,caretPosition) { 418 if (nameSpaceString===undefined) { nameSpaceString=this.getSourceValue(); } 419 if (typeOf(caretPosition)!="number") { 420 caretPosition=this.getSourceCaretPosition(); 421 } 422 var namespace = []; 423 var allowed = this.options.namespaceAllowed; 424 425 /* parser start */ 426 var positionStart = 0; 427 var i = 0; 428 for (i=caretPosition-1;i>0;--i) { 429 var character = nameSpaceString.charAt(i); 430 var previousCharacter = nameSpaceString.charAt(i+1); 431 if (character===undefined) { 432 break; 433 } 434 if (character=="." && previousCharacter==".") { 435 positionStart = i+1+1; 436 break; 437 } 438 var cursorJump = 0; 439 var endsWithAllowed = allowed.some(function(item) { 440 if (item.length==1) { 441 return character==item; 442 } 443 else if (nameSpaceString.substring(i-item.length+1,i+1)==item) { 444 //cursorJump = item.length+1; 445 cursorJump = item.length-1; 446 return true; 447 } 448 else if (nameSpaceString.substring(i,i+item.length)==item) { 449 return true; 450 } 451 }); 452 if (cursorJump>0) { 453 i = i-cursorJump; 454 character=nameSpaceString[i]; 455 previousCharacter=nameSpaceString.charAt(i+1); 456 continue; 457 } 458 if ( character!="." && !(character.test(/^\w$/) || endsWithAllowed ) ) { 459 positionStart = i+1; 460 if (previousCharacter!==undefined) { 461 var jumpPrevious = 0; 462 if (previousCharacter==".") { 463 //if theres a dot ".", just move forward of 1 position and exit the loop. 464 jumpPrevious = 1; 465 positionStart = i+1+jumpPrevious; 466 break; 467 } 468 var previousCharacterEndsWithAllowed = allowed.some(function(item) { 469 if (item.length==1) { 470 if (previousCharacter==item) { 471 jumpPrevious=1; 472 return true; 473 } 474 } 475 //forward seek 476 else if (nameSpaceString.substring(i,i+item.length) == item ) { 477 jumpPrevious=item.length; 478 return true; 479 } 480 //back seek 481 else if (nameSpaceString.substring(i-item.length+1,i+1) == item) { 482 jumpPrevious= (-(item.length)); 483 return true; 484 } 485 }); 486 487 if (!previousCharacterEndsWithAllowed && !previousCharacter.test(/^\w$/)) { 488 //here only allowed 489 positionStart = i+1+jumpPrevious; 490 } 491 } 492 break; 493 } 494 } 495 if(positionStart>caretPosition) { 496 positionStart=caretPosition; 497 } 498 nameSpaceString = nameSpaceString.substring(positionStart,caretPosition).trim(); 499 if (nameSpaceString.length>0) { 500 namespace=nameSpaceString.split("."); 501 if (namespace[namespace.length-1]==="") { 502 namespace[namespace.length-1] = "/"; 503 } 504 } 505 else { 506 namespace=["/"]; 507 } 508 return namespace; 509 /* parser end */ 510 }, 511 _setItemSelected: function(item, executeScroll) { 512 if (item!==null) { 513 var oldItem = this.getItemSelected(); 514 if (oldItem!==null) { oldItem.removeClass(this.options.css.itemSelected); } 515 item.addClass(this.options.css.itemSelected); 516 this.fireEvent("selectItem",item); 517 if (executeScroll !== false) { 518 this.scrollToItem(item); 519 } 520 } 521 }, 522 _setSourceCaretPosition: function() { 523 this.options.source.store("MooContentAssist-CaretPosition",this.options.source.getCaretPosition()); 524 }, 525 _useItemSelected: function() { 526 var text = this.getItemSelected(); 527 var w = this.getAssistWindow(); 528 if (text!==null && w!==null) { 529 text = text.retrieve("value"); 530 var textarea = this.options.source; 531 var scrollTop = textarea.scrollTop; 532 var position = this.getSourceCaretPosition(); 533 var namespace = this.getNameSpace().getLast(); 534 var completedText=null; 535 if (namespace=="/") { 536 completedText=text; 537 } 538 else { 539 completedText = text.substring(namespace.length,text.length); 540 } 541 var adjustCaseText = text.substring(0,text.length-completedText.length); 542 var textbefore = textarea.get("value").substring(0, position); 543 textbefore = textbefore.substring(0,textbefore.length-adjustCaseText.length)+adjustCaseText; 544 var textafter = textarea.get("value").substring(position); 545 textarea.set("value", textbefore + completedText + textafter); 546 textarea.setCaretPosition(textbefore.length + completedText.length); 547 textarea.scrollTop = scrollTop; 548 this.fireEvent("useItem",text); 549 this.fireEvent("end",this); 550 } 551 }, 552 createAssistWindow: function() { 553 var w = new Element("div",{ 554 "class": "MooContentAssist" 555 }); 556 557 this.options.source.store("MooContentAssist",w); 558 var itemsEventsObj = {}; 559 itemsEventsObj['click:relay(.'+this.options.css.item+')'] = function(ev){ 560 ev.stopPropagation(); 561 ev.preventDefault(); 562 this._useItemSelected(); 563 }.bind(this); 564 itemsEventsObj['mouseover:relay(.'+this.options.css.item+')'] = function(ev){ 565 ev.stopPropagation(); 566 ev.preventDefault(); 567 if (ev.target.get("tag")==this.options.itemType&&ev.target.hasClass(this.options.css.item)) { 568 this._setItemSelected(ev.target,false); 569 } 570 else { 571 var parent = ev.target.getParent(this.options.itemType+"."+this.options.css.item); 572 if (parent!==null) { 573 this._setItemSelected(parent,false); 574 } 575 576 } 577 }.bind(this); 578 w.addEvents(itemsEventsObj); 579 var sourceEl = this.options.source; 580 var sourceElPosition=sourceEl.getPosition(); 581 var sourceElSize = sourceEl.getDimensions(); 582 w.inject(this.options.source,"after"); 583 var top = sourceElPosition.y+sourceElSize.height+this.options.windowPadding.y; 584 var left= sourceElPosition.x+this.options.windowPadding.x; 585 w.setStyles({ 586 "overflow": "auto", 587 "width": sourceElSize.width, 588 "left": left, 589 "top": top 590 }); 591 592 if (this.options.itemsContainerType!==null) { 593 new Element(this.options.itemsContainerType, { 594 "class": this.options.css.itemsContainer 595 }).inject(w,"bottom"); 596 } 597 return w; 598 }, 599 end: function() { 600 var mca = this.getAssistWindow(); 601 if (mca !== null) { 602 mca.destroy(); 603 this.options.source.store("MooContentAssist",null); 604 } 605 }, 606 getAssistWindow: function() { 607 return this.options.source.retrieve("MooContentAssist"); 608 }, 609 getItemSelected: function() { 610 var w = this.getAssistWindow(); 611 var item = null; 612 if (w!==null) { 613 item = w.getElement(this._prefixItemsSelector+this.options.css.itemSelected); 614 } 615 return item; 616 }, 617 getNameSpace: function(string) { 618 var namespace = []; 619 namespace = this._namespaceParser(string); 620 return namespace; 621 }, 622 getRenderedWord: function(word) { 623 return this.options.vocabularyManager_Render.call(this,word); 624 }, 625 getSourceCaretPosition: function() { 626 var pos = this.options.source.retrieve("MooContentAssist-CaretPosition"); 627 if (pos === null) { 628 pos = this.options.source.getCaretPosition(); 629 } 630 return pos; 631 }, 632 getSourceValue: function() { 633 return this.options.source.get("value"); 634 }, 635 getVocabulary: function(namespace) { 636 var extractedVocabulary = this.options.vocabularyManager_GetVocabulary.call(this,namespace); 637 return extractedVocabulary; 638 }, 639 hide: function() { 640 if(this.getAssistWindow()!==null) this.getAssistWindow().dissolve(); 641 this.fireEvent("hide"); 642 }, 643 initialize: function(opt) { 644 this.setOptions(opt); 645 if (opt.itemsContainerType===null) { 646 this.options.itemsContainerType = null; 647 this._prefixItemsSelector="."; 648 } 649 else { 650 this._prefixItemsSelector="."+this.options.css.itemsContainer+" ."; 651 } 652 this.options.source.store("MooContentAssist",null); 653 this._eventManager(); 654 this.oldNamespace=false; 655 }, 656 scrollToItem: function(item) { 657 var w = this.getAssistWindow(); 658 if (w!==null && item!==null) { 659 var animationScroller = w.retrieve("MooContentAssist-AnimationScroller"); 660 if (animationScroller === null) { 661 animationScroller = new Fx.Scroll(w,{ 662 duration: this.options.animationDuration, 663 offset: {"x": 0, "y": w.getStyle('padding-top').toInt()*-1} 664 }); 665 w.store("MooContentAssist-AnimationScroller",animationScroller); 666 } 667 //item height 668 var i = item.getComputedSize({"styles": ["margin","padding","border"]}).totalHeight; 669 //box height 670 var f = (w.getComputedSize({"styles": ["padding"]}).totalHeight/i).toInt(); 671 //children 672 var children = w.getElements(this._prefixItemsSelector+this.options.css.item); 673 //current item 674 var c = children.indexOf(item); 675 //index 676 var indexToScrollTo = ((c/f).toInt()) * f; 677 //calculate the current "frame" 678 if (c > (f/2).toInt()) { 679 indexToScrollTo = c - (f/2).toInt(); 680 } 681 //scroll to item at that index 682 if(w.getElement(children[indexToScrollTo])!==null) { 683 try { 684 animationScroller.toElement(children[indexToScrollTo]); 685 } catch(e) { 686 //sometimes IE fires errors... 687 w.store("MooContentAssist-AnimationScroller",null); 688 } 689 } 690 } 691 }, 692 selectItemDown: function() { 693 var currentItem = this.getItemSelected(); 694 var prevItem = null; 695 if (currentItem!==null) { 696 prevItem = currentItem.getNext(); 697 } 698 else { 699 prevItem = this.getAssistWindow().getFirst(this._prefixItemsSelector+this.options.css.item); 700 } 701 if (prevItem!==null) { 702 this._setItemSelected(prevItem); 703 } 704 else { 705 this._setItemSelected(this.getAssistWindow().getFirst(this._prefixItemsSelector+this.options.css.item)); 706 } 707 }, 708 selectItemUp: function() { 709 var currentItem = this.getItemSelected(); 710 var prevItem = null; 711 if (currentItem!==null) { 712 prevItem = currentItem.getPrevious(); 713 } 714 else { 715 prevItem = this.getAssistWindow().getLast(this._prefixItemsSelector+this.options.css.item); 716 } 717 if (prevItem!==null) { 718 this._setItemSelected(prevItem); 719 } 720 else { 721 this._setItemSelected(this.getAssistWindow().getLast(this._prefixItemsSelector+this.options.css.item)); 722 } 723 }, 724 setAggressiveAssist: function(aggressiveStatus) { 725 if (typeOf(aggressiveStatus)=="boolean"){ 726 this.options.aggressiveAssist=aggressiveStatus; 727 } 728 }, 729 setAssistWindowContent: function(vocabulary) { 730 var w = this.getAssistWindow(); 731 if (w!==null) { 732 vocabulary = Array.from(vocabulary); 733 734 var injectBindElement = this.options.itemsContainerType===null? w : w.getElement("."+this.options.css.itemsContainer); 735 var inject = function(word) { 736 word.inject(this); 737 }.bind(injectBindElement); 738 for (var i=0;i<vocabulary.length;i++) { 739 var currentWord = vocabulary[i]; 740 inject(currentWord); 741 } 742 this.setFrameSize(); 743 this.selectItemDown(); 744 } 745 }, 746 setFrameSize: function(size) { 747 if(typeOf(size) != "number") { size = this.options.frameSize; } 748 var selector = this._prefixItemsSelector+this.options.css.item; 749 var w = this.getAssistWindow(); 750 var children = w.getElements(selector); 751 var childrenLength = children.length > 0 ? children.length : 1; 752 if (childrenLength<size) { size = childrenLength;} 753 var exampleItem = w.getElement(selector); 754 if (exampleItem===null) { 755 exampleItem = w.getElement(this._prefixItemsSelector+this.options.css.messageItem); 756 } 757 w.setStyle("height",(exampleItem.getComputedSize({ 758 "styles": ["padding","margin","border"] 759 }).totalHeight * size) + "px"); 760 }, 761 show: function() { 762 if(this.getAssistWindow()!==null) this.getAssistWindow().reveal(); 763 this.getAssistWindow().setStyle("opacity",1); 764 this.fireEvent("show"); 765 }, 766 start: function() { 767 var mca = this.getAssistWindow(); 768 if (mca!==null) { 769 this.end(); 770 this.createAssistWindow(); 771 } 772 var value = this.getSourceValue(); 773 var namespace = this.getNameSpace(value); 774 var vocabulary = null; 775 if (namespace == this.oldNamespace) { 776 vocabulary = this.oldVocabulary; 777 } 778 else { 779 vocabulary = this.getVocabulary(namespace); 780 this.oldVocabulary = vocabulary; 781 } 782 if (this.options.vocabularyDiscoverer) { 783 var userVocabulary = this._discoverUserVocabulary(namespace); 784 vocabulary = this._mergeVocabulary(vocabulary,userVocabulary); 785 } 786 if (vocabulary.length > 0) { 787 var renderedVocabulary = []; 788 vocabulary.each(function(word) { 789 renderedVocabulary.push(this.getRenderedWord(word)); 790 }.bind(this)); 791 if (mca === null) { 792 mca = this.createAssistWindow(); 793 } 794 this.setAssistWindowContent(renderedVocabulary); 795 } 796 else { 797 if (!this.options.aggressiveAssist || (this.options.aggressiveAssist && namespace.length>1)) { 798 var messageEl = this._createMessage(this.options.labels.nothingFound); 799 if (mca === null) { 800 mca = this.createAssistWindow(); 801 } 802 this.setAssistWindowContent(messageEl); 803 } 804 else { 805 this.end(); 806 } 807 } 808 } 809 }); 810