1/* globals define, angular */ 2(function (root, factory) { 3 if (typeof define === 'function' && define.amd) { 4 define(['quill'], factory) 5 } else if (typeof module !== 'undefined' && typeof exports === 'object') { 6 module.exports = factory(require('quill')) 7 } else { 8 root.Requester = factory(root.Quill) 9 } 10}(this, function (Quill) { 11 'use strict' 12 13 var app 14 // declare ngQuill module 15 app = angular.module('ngQuill', ['ngSanitize']) 16 17 app.provider('ngQuillConfig', function () { 18 var config = { 19 modules: { 20 toolbar: [ 21 ['bold', 'italic', 'underline', 'strike'], // toggled buttons 22 ['blockquote', 'code-block'], 23 24 [{ 'header': 1 }, { 'header': 2 }], // custom button values 25 [{ 'list': 'ordered' }, { 'list': 'bullet' }], 26 [{ 'script': 'sub' }, { 'script': 'super' }], // superscript/subscript 27 [{ 'indent': '-1' }, { 'indent': '+1' }], // outdent/indent 28 [{ 'direction': 'rtl' }], // text direction 29 30 [{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown 31 [{ 'header': [1, 2, 3, 4, 5, 6, false] }], 32 33 [{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme 34 [{ 'font': [] }], 35 [{ 'align': [] }], 36 37 ['clean'], // remove formatting button 38 39 ['link', 'image', 'video'] // link and image, video 40 ] 41 }, 42 bounds: document.body, 43 debug: 'warn', 44 theme: 'snow', 45 scrollingContainer: null, 46 placeholder: 'Insert text here ...', 47 readOnly: false, 48 trackChanges: 'user', 49 preserveWhitespace: false 50 } 51 52 this.set = function (customConf) { 53 customConf = customConf || {} 54 55 if (customConf.modules) { 56 config.modules = customConf.modules 57 } 58 if (customConf.theme) { 59 config.theme = customConf.theme 60 } 61 if (customConf.placeholder !== null && customConf.placeholder !== undefined) { 62 config.placeholder = customConf.placeholder.trim() 63 } 64 if (customConf.readOnly) { 65 config.readOnly = customConf.readOnly 66 } 67 if (customConf.formats) { 68 config.formats = customConf.formats 69 } 70 if (customConf.bounds) { 71 config.bounds = customConf.bounds 72 } 73 if (customConf.scrollingContainer) { 74 config.scrollingContainer = customConf.scrollingContainer 75 } 76 if (customConf.debug ||Â customConf.debug === false) { 77 config.debug = customConf.debug 78 } 79 if (customConf.trackChanges && ['all', 'user'].indexOf(customConf.trackChanges) > -1) { 80 config.trackChanges = customConf.trackChanges 81 } 82 if (customConf.preserveWhitespace) { 83 config.preserveWhitespace = true 84 } 85 } 86 87 this.$get = function () { 88 return config 89 } 90 }) 91 92 app.component('ngQuillEditor', { 93 bindings: { 94 'modules': '<modules', 95 'theme': '@?', 96 'readOnly': '<?', 97 'format': '@?', 98 'debug': '@?', 99 'formats': '<?', 100 'placeholder': '<?', 101 'bounds': '<?', 102 'scrollingContainer': '<?', 103 'strict': '<?', 104 'onEditorCreated': '&?', 105 'onContentChanged': '&?', 106 'onBlur': '&?', 107 'onFocus': '&?', 108 'onSelectionChanged': '&?', 109 'ngModel': '<', 110 'maxLength': '<', 111 'minLength': '<', 112 'customOptions': '<?', 113 'styles': '<?', 114 'sanitize': '<?', 115 'customToolbarPosition': '@?', 116 'trackChanges': '@?', 117 'preserveWhitespace': '<?', 118 }, 119 require: { 120 ngModelCtrl: 'ngModel' 121 }, 122 transclude: { 123 'toolbar': '?ngQuillToolbar' 124 }, 125 template: '<div class="ng-hide" ng-show="$ctrl.ready"><ng-transclude ng-transclude-slot="toolbar"></ng-transclude></div>', 126 controller: ['$scope', '$element', '$sanitize', '$timeout', '$transclude', 'ngQuillConfig', function ($scope, $element, $sanitize, $timeout, $transclude, ngQuillConfig) { 127 var config = {} 128 var content 129 var editorElem 130 var format = 'html' 131 var editorChanged = false 132 var editor 133 var placeholder = ngQuillConfig.placeholder 134 var textChangeEvent 135 var selectionChangeEvent 136 137 this.setter = function (value) { 138 if (format === 'html') { 139 return editor.clipboard.convert(this.sanitize ? $sanitize(value) : value) 140 } else if (this.format === 'json') { 141 try { 142 return JSON.parse(value) 143 } catch (e) { 144 return [{ insert: value }] 145 } 146 } 147 148 return value 149 } 150 151 this.validate = function (text) { 152 var textLength = text.trim().length 153 154 if (this.maxLength) { 155 if (textLength > this.maxLength) { 156 this.ngModelCtrl.$setValidity('maxlength', false) 157 } else { 158 this.ngModelCtrl.$setValidity('maxlength', true) 159 } 160 } 161 162 if (this.minLength > 0) { 163 if (textLength < this.minLength && textLength) { 164 this.ngModelCtrl.$setValidity('minlength', false) 165 } else { 166 this.ngModelCtrl.$setValidity('minlength', true) 167 } 168 } 169 } 170 171 this.$onChanges = function (changes) { 172 if (changes.ngModel) { 173 content = changes.ngModel.currentValue 174 175 if (editor) { 176 if (!editorChanged) { 177 if (content) { 178 if (changes.ngModel.currentValue !== changes.ngModel.previousValue) { 179 if (this.format === 'text') { 180 editor.setText(content) 181 } else { 182 editor.setContents( 183 this.setter(content) 184 ) 185 } 186 } 187 } else { 188 editor.setText('') 189 } 190 } 191 editorChanged = false 192 } 193 } 194 195 if (editor && changes.readOnly) { 196 editor.enable(!changes.readOnly.currentValue) 197 } 198 199 if (editor && changes.placeholder) { 200 editor.root.dataset.placeholder = changes.placeholder.currentValue 201 } 202 203 if (editor && editorElem && changes.styles) { 204 var currentStyling = changes.styles.currentValue 205 var previousStyling = changes.styles.previousValue 206 207 if (previousStyling) { 208 for (var key in previousStyling) { 209 editorElem.style[key] = '' 210 } 211 } 212 if (currentStyling) { 213 for (var activeStyle in currentStyling) { 214 if (currentStyling.hasOwnProperty(activeStyle)) { 215 editorElem.style[activeStyle] = currentStyling[activeStyle] 216 } 217 } 218 } 219 } 220 } 221 222 this.$onInit = function () { 223 if (this.placeholder !== null && this.placeholder !== undefined) { 224 placeholder = this.placeholder.trim() 225 } 226 227 if (this.format && ['object', 'html', 'text', 'json'].indexOf(this.format) > -1) { 228 format = this.format 229 } 230 231 config = { 232 theme: this.theme || ngQuillConfig.theme, 233 readOnly: this.readOnly || ngQuillConfig.readOnly, 234 modules: this.modules || ngQuillConfig.modules, 235 formats: this.formats || ngQuillConfig.formats, 236 placeholder: placeholder,
237 bounds: this.bounds || ngQuillConfig.bounds, 238 strict: this.strict, 239 scrollingContainer: this.scrollingContainer || ngQuillConfig.scrollingContainer, 240 debug: this.debug || this.debug === false ? this.debug : ngQuillConfig.debug 241 } 242 } 243 244 this.$postLink = function () { 245 // create quill instance after dom is rendered 246 $timeout(function () { 247 this._initEditor() 248 }.bind(this), 0) 249 } 250 251 this.$onDestroy = function () { 252 editor = null 253 254 if (textChangeEvent) { 255 textChangeEvent.removeListener('text-change') 256 } 257 if (selectionChangeEvent) { 258 selectionChangeEvent.removeListener('selection-change') 259 } 260 } 261 262 this._initEditor = function () { 263 var $editorElem = this.preserveWhitespace ? angular.element('<pre></pre>') : angular.element('<div></div>') 264 var container = $element.children() 265 266 editorElem = $editorElem[0] 267 268 if (config.bounds === 'self') { 269 config.bounds = editorElem 270 } 271 272 // set toolbar to custom one 273 if ($transclude.isSlotFilled('toolbar')) { 274 config.modules.toolbar = container.find('ng-quill-toolbar').children()[0] 275 } 276 277 if (this.styles) { 278 for (var activeStyle in this.styles) { 279 if (this.styles.hasOwnProperty(activeStyle)) { 280 editorElem.style[activeStyle] = this.styles[activeStyle] 281 } 282 } 283 } 284 285 if (!this.customToolbarPosition || this.customToolbarPosition === 'top') { 286 container.append($editorElem) 287 } else { 288 container.prepend($editorElem) 289 } 290 291 if (this.customOptions) { 292 this.customOptions.forEach(function (customOption) { 293 var newCustomOption = Quill.import(customOption.import) 294 newCustomOption.whitelist = customOption.whitelist 295 if (customOption.toRegister) { 296 newCustomOption[customOption.toRegister.key] = customOption.toRegister.value 297 } 298 Quill.register(newCustomOption, true) 299 }) 300 } 301 302 editor = new Quill(editorElem, config) 303 304 this.ready = true 305 306 // mark model as touched if editor lost focus 307 selectionChangeEvent = editor.on('selection-change', function (range, oldRange, source) { 308 if (range === null && this.onBlur) { 309 this.onBlur({ 310 editor: editor, 311 source: source 312 }) 313 } else if (oldRange === null && this.onFocus) { 314 this.onFocus({ 315 editor: editor, 316 source: source 317 }) 318 } 319 320 if (this.onSelectionChanged) { 321 this.onSelectionChanged({ 322 editor: editor, 323 oldRange: oldRange, 324 range: range, 325 source: source 326 }) 327 } 328 329 if (range) { 330 return 331 } 332 $scope.$applyAsync(function () { 333 this.ngModelCtrl.$setTouched() 334 }.bind(this)) 335 }.bind(this)) 336 337 // update model if text changes 338 textChangeEvent = editor.on('text-change', function (delta, oldDelta, source) { 339 var html = editorElem.querySelector('.ql-editor').innerHTML 340 var text = editor.getText() 341 var content = editor.getContents() 342 343 var emptyModelTag = ['<' + editor.root.firstChild.localName + '>', '</' + editor.root.firstChild.localName + '>'] 344 345 if (html === emptyModelTag[0] + '<br>' + emptyModelTag[1]) { 346 html = null 347 } 348 this.validate(text) 349 350 $scope.$applyAsync(function () { 351 var trackChanges = this.trackChanges || ngQuillConfig.trackChanges 352 if (source === 'user' || trackChanges && trackChanges === 'all') { 353 editorChanged = true 354 if (format === 'text') { 355 // if nothing changed $ngOnChanges is not called again 356 // But we have to reset editorChanged flag 357 if (text === this.ngModelCtrl.$viewValue) { 358 editorChanged = false 359 } else { 360 this.ngModelCtrl.$setViewValue(text) 361 } 362 } else if (format === 'object') { 363 this.ngModelCtrl.$setViewValue(content) 364 } else if (this.format === 'json') { 365 try { 366 this.ngModelCtrl.$setViewValue(JSON.stringify(content)) 367 } catch (e) { 368 this.ngModelCtrl.$setViewValue(text) 369 } 370 } else { 371 this.ngModelCtrl.$setViewValue(html) 372 } 373 } 374 375 if (this.onContentChanged) { 376 this.onContentChanged({ 377 editor: editor, 378 html: html, 379 text: text, 380 content: content, 381 delta: delta, 382 oldDelta: oldDelta, 383 source: source 384 }) 385 } 386 }.bind(this)) 387 }.bind(this)) 388 389 // set initial content 390 if (content) { 391 if (format === 'text') { 392 editor.setText(content, 'silent') 393 } else if (format === 'object') { 394 editor.setContents(content, 'silent') 395 } else if (format === 'json') { 396 try { 397 editor.setContents(JSON.parse(content), 'silent') 398 } catch (e) { 399 editor.setText(content, 'silent') 400 } 401 } else { 402 editor.setContents(editor.clipboard.convert(this.sanitize ? $sanitize(content) : content, 'silent')) 403 } 404 405 editor.history.clear() 406 } 407 this.validate(editor.getText()) 408 409 // provide event to get informed when editor is created -> pass editor object. 410 if (this.onEditorCreated) { 411 this.onEditorCreated({editor: editor}) 412 } 413 } 414 }] 415 }) 416 417 return app.name 418}))
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.