1ComposerYnComment.Plugin.Nctag = new Class({ 2 Extends: ComposerYnComment.Plugin.Interface, 3 name: 'tag', 4 options: { 5 'enabled': false, 6 requestOptions: {}, 7 'suggestOptions': { 8 'minLength': 1, 9 'maxChoices': 100, 10 'delay': 250, 11 'selectMode': 'pick', 12 'multiple': false, 13 'filterSubset': true, 14 'tokenFormat': 'object', 15 'tokenValueKey': 'label', 16 'injectChoice': $empty, 17 'onPush': $empty, 18 'prefetchOnInit': true, 19 'alwaysOpen': false, 20 'ignoreKeys': true, 21 'stopKeysEvent': false 22 } 23 }, 24 initialize: function(options) { 25 this.params = new Hash(this.params); 26 this.parent(options); 27 }, 28 suggest: false, 29 attach: function() { 30 this.parent(); 31 // Key Events 32 var self = this; 33 34 this.getComposer().addEvent((1) ? 'editorKeyDown' : 'editorKeyPress', 35 function(event) { 36 if (self.suggest && self.suggest.visible && event) { 37 self.suggest.onCommand(event); 38 if (self.suggest.stopKeysEvent) { 39 event.stop(); 40 return; 41 } 42 } 43 self.monitor.bind(self)(event); 44 } 45 ); 46 this.getComposer().addEvent('editorClick', this.monitor.bind(this)); 47 48 // Submit 49 this.getComposer().addEvent('editorSubmit', this.submit.bind(this)); 50 return this; 51 }, 52 detach: function() { 53 if (!this.options.enabled) 54 return; 55 this.parent(); 56 this.getComposer().removeEvent('editorKeyPress', this.monitor.bind(this)); 57 this.getComposer().removeEvent('editorClick', this.monitor.bind(this)); 58 this.getComposer().removeEvent('editorSubmit', this.submit.bind(this)); 59 if (this.interval) 60 $clear(this.interval); 61 return this; 62 }, 63 activate: $empty, 64 deactivate: $empty, 65 poll: function() { 66 67 }, 68 monitor: function(e) { 69 // seems like we have to do this stupid delay or otherwise the last key 70 // doesn't get in the content 71 (function() { 72 var selection = this.getComposer().selection; 73 var range = selection.getRange(); 74 if (!range) 75 return; 76 var start = 0; 77 if (window.getSelection) { 78 start = range.startOffset; 79 } else if (document.selection) { 80 var range_t = document.selection.createRange(); 81 if (range_t == null || range_t['text'] == null) { 82 } else { 83 var textlength = selection.win.innerText.length; 84 range.moveStart("character", -textlength); 85 start = range.text.length; 86 } 87 } 88 89 var content = null; 90 if (range.startContainer) { 91 content = range.startContainer.data; 92 } else if (selection.win) { 93 content = selection.win.innerText; 94 } 95 if (!content) 96 return; 97 98 this.filterYnCommentTagsFromComposer(); 99 100 var currentIndex = false; 101 content = content.substring(0, start); 102 currentIndex = content.lastIndexOf('@'); 103 if (currentIndex == -1 || currentIndex >= start) { 104 this.endSuggest(); 105 return; 106 } 107 // Get the current at segment 108 var segment = content.substring(currentIndex + 1, start); 109 if (segment.length < 1) { 110 this.endSuggest(); 111 return; 112 } 113 114 this.doSuggest(segment); 115 116 }).delay(250, this); 117 }, 118 doSuggest: function(text) 119 { 120 this.currentText = text; 121 var tagged = ''; 122 this.getComposer().elements.body.getElements('.yncomment_composer_tag').each(function(tag) 123 { 124 tagged += tag.get('rel') + ','; 125 }); 126 this.tagged = tagged;
127 var suggest = this.getSuggest(); 128 var input = this.getHiddenInput(); 129 input.set('value', text); 130 input.value = text; 131 suggest.prefetch(); 132 }, 133 endSuggest: function() { 134 this.currentText = ''; 135 this.tagged = ''; 136 this.positions = {}; 137 if (this.suggest) { 138 this.getSuggest().destroy(); 139 delete this.suggest; 140 } 141 }, 142 getHiddenInput: function() { 143 if (!this.hiddenInput) { 144 this.hiddenInput = new Element('input', { 145 'type': 'text', 146 'styles': { 147 'display': 'none' 148 } 149 }).inject(document.body); 150 } 151 return this.hiddenInput; 152 }, 153 getSuggest: function() { 154 if (!this.suggest) { 155 var width = this.getComposer().elements.body.getSize().x; 156 157 this.choices = new Element('ul', { 158 'class': 'tag-autosuggest yncomment-autosuggest', 159 'styles': { 160 'width': (width - 2) + 'px' 161 } 162 }).inject(this.getComposer().elements.body, 'after'); 163 164 // Check offset 165 var global_content = this.choices.getParent('#global_content'); 166 var y_position = this.choices.getPosition(global_content).y; 167 var p_height = global_content.getHeight(); 168 var c_height = 375; 169 if(p_height - y_position < c_height) 170 { 171 this.choices.addClass('users_suggest_reverse'); 172 } 173 174 var self = this; 175 var options = $merge(this.options.suggestOptions, { 176 'customChoices': this.choices, 177 'injectChoice': function(token) { 178 var choice = new Element('li', { 179 'class': 'autocompleter-choices', 180 'html': token.photo || '', 181 'id': token.guid 182 }); 183 var divEl = new Element('div', { 184 'html': this.markQueryValue(token.label) + '<span class = "yc_tag_' + token.type + '"></span>', 185 'class': 'autocompleter-choice' 186 }); 187 if (token.type != 'user') 188 { 189 new Element('div', { 190 'html': token.type 191 }).inject(divEl); 192 } 193 divEl.inject(choice); 194 new Element('input', { 195 'type': 'hidden', 196 'value': JSON.encode(token) 197 }).inject(choice); 198 this.addChoiceEvents(choice).inject(this.choices); 199 choice.store('autocompleteChoice', token); 200 }, 201 'onChoiceSelect': function(choice) { 202 var data = JSON.decode(choice.getElement('input').value); 203 var composer = self.getComposer(); 204 var body = composer.elements.body; 205 206 var content = null; 207 if (composer._supportsContentEditable()) { 208 content = composer.elements.body.get('html'); 209 } else { 210 content = composer.elements.body.get('value'); 211 } 212 213 var replaceString = '@' + self.currentText; 214 var newString = '<span class="yncomment_composer_tag" rel="' + data.guid + '" rev="' + data.label + '" >' + data.label + '</span> '; 215 216 content = content.replace(/\<span\>\<\/span\>/ig, ''); 217 content = content.replace(new RegExp(replaceString, 'i'), newString); 218 composer.setContent(content); 219 220 var tagElement = self.getYnCommentTagElementFromComposer(data.label); 221 222 if (window.getSelection) { // all browsers, except IE before version 9 223 composer.selection.getSelection().collapse(tagElement.nextSibling, 1); 224 } else if (document.selection) { // Internet Explorer before version 9 225 composer.selection.getRange().moveToElementText(tagElement); 226 } 227 228 }, 229 'emptyChoices': function() { 230 this.fireEvent('onHide', [this.element, this.choices]); 231 }, 232 'onCommand': function(e) { 233 // This code is copy to Autocompleter JS amd hack minor for check that stop the key event 234 if (e && e.key && !e.shift) { 235 switch (e.key) { 236 case 'enter': 237 e.stop(); 238 if (!this.selected) { 239 if (!this.options.customChoices) { 240 // @todo support multiple 241 this.element.value = ''; 242 } 243 return true; 244 } 245 if (this.selected && this.visible) { 246 this.stopKeysEvent = true; 247 this.choiceSelect(this.selected); 248 return !!(this.options.autoSubmit); 249 } 250 break; 251 case 'up': 252 case 'down': 253 var value = this.element.value; 254 if (!this.prefetch() && this.queryValue !== null) { 255 this.stopKeysEvent = true; 256 var up = (e.key == 'up'); 257 if (this.selected) 258 this.selected.removeClass('autocompleter-selected'); 259 if (!(this.selected)[ 260 ((up) ? 'getPrevious' : 'getNext') 261 ](this.options.choicesMatch)) { 262 this.selected = null; 263 } 264 265 this.choiceOver( 266 (this.selected || this.choices)[ 267 (this.selected) ? ((up) ? 'getPrevious' : 'getNext') : ((up) ? 'getLast' : 'getFirst') 268 ](this.options.choicesMatch), true); 269 this.element.value = value; 270 } 271 return false;
272 case 'esc': 273 this.stopKeysEvent = true; 274 this.hideChoices(true); 275 if (!this.options.customChoices) 276 this.element.value = ''; 277 break; 278 case 'tab': 279 this.stopKeysEvent = true; 280 if (this.selected && this.visible) { 281 this.choiceSelect(this.selected); 282 return !!(this.options.autoSubmit); 283 } else { 284 this.hideChoices(true); 285 if (!this.options.customChoices) 286 this.element.value = ''; 287 break; 288 } 289 default : 290 this.stopKeysEvent = false; 291 } 292 } 293 294 } 295 }); 296 297 if (this.options.suggestProto == 'local') { 298 this.suggest = new Autocompleter.Local(this.getHiddenInput(), this.options.suggestParam, options); 299 } else if (this.options.suggestProto == 'request.json') 300 { 301 var suggestUrl = this.options.suggestOptions.url; 302 if(this.tagged) 303 { 304 suggestUrl = suggestUrl + '/tagged/' + this.tagged; 305 } 306 this.suggest = new Autocompleter.Request.JSON(this.getHiddenInput(), suggestUrl, options); 307 } 308 if (this.suggest && !this.suggest.options.alwaysOpen) { 309 this.getComposer().elements.body 310 .addEvent('blur', this.suggestHideChoices.create({ 311 bind: this 312 })); 313 } 314 } 315 316 return this.suggest; 317 }, 318 suggestHideChoices: function() { 319 var suggest = this.suggest; 320 if (suggest) { 321 suggest.hideChoices(true); 322 if (!suggest.options.customChoices) 323 suggest.element.value = ''; 324 } 325 }, 326 filterYnCommentTagsFromComposer: function() 327 { 328 var body = this.getComposer().elements.body; 329 body.getElements('.yncomment_composer_tag').each(function(tag) { 330 if (tag.get('text') != tag.get('rev')) { 331 tag.removeClass('yncomment_composer_tag'); 332 } 333 }); 334 }, 335 getYnCommentTagElementFromComposer: function(text) { 336 var body = this.getComposer().elements.body; 337 var element = null; 338 body.getElements('.yncomment_composer_tag').each(function(tag) 339 { 340 if (tag.get('text') == tag.get('rev') && tag.get('text') == text.replace(/­/ig, 'Â').replace(/'/ig, '\'')) { 341 element = tag; 342 } 343 }); 344 return element; 345 }, 346 submit: function() { 347 this.makeFormInputs({ 348 tag: this.getYnCommentTagsFromComposer().toQueryString() 349 }); 350 }, 351 // get the tags which are in composer 352 getYnCommentTagsFromComposer: function() 353 { 354 // filter the tags which are uncorrect formate or lable 355 this.filterYnCommentTagsFromComposer(); 356 357 var composerTags = new Hash(); 358 var body = this.getComposer().elements.body; 359 body.getElements('.yncomment_composer_tag').each(function(tag) { 360 composerTags[tag.get('rel')] = tag.get('text'); 361 }); 362 return composerTags; 363 }, 364 makeFormInputs: function(data) { 365 $H(data).each(function(value, key) { 366 this.setFormInputValue(key, value); 367 }.bind(this)); 368 }, 369 // make tag hidden input and set value into composer form 370 setFormInputValue: function(key, value) { 371 var elName = 'yncommentComposerForm' + key.capitalize(); 372 var composerObj = this.getComposer(); 373 if (composerObj.elements.has(elName)) 374 composerObj.elements.get(elName).destroy(); 375 composerObj.elements.set(elName, new Element('input', { 376 'type': 'hidden', 377 'name': 'composer[' + key + ']', 378 'value': value || '' 379 }).inject(composerObj.getInputArea())); 380 composerObj.elements.get(elName).value = value; 381 } 382});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.