]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/_editor/plugins/FontChoice.js.uncompressed.js
modify dojo rebuild script to remove uncompressed files
[tt-rss.git] / lib / dijit / _editor / plugins / FontChoice.js.uncompressed.js
CommitLineData
f0cfe83e
AD
1define("dijit/_editor/plugins/FontChoice", [
2 "dojo/_base/array", // array.indexOf array.map
3 "dojo/_base/declare", // declare
4 "dojo/dom-construct", // domConstruct.place
5 "dojo/i18n", // i18n.getLocalization
6 "dojo/_base/lang", // lang.delegate lang.hitch lang.isString
7 "dojo/store/Memory", // MemoryStore
8 "../../registry", // registry.getUniqueId
9 "../../_Widget",
10 "../../_TemplatedMixin",
11 "../../_WidgetsInTemplateMixin",
12 "../../form/FilteringSelect",
13 "../_Plugin",
14 "../range",
15 "dojo/i18n!../nls/FontChoice"
16], function(array, declare, domConstruct, i18n, lang, MemoryStore,
17 registry, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, FilteringSelect, _Plugin, rangeapi){
18
19
20// module:
21// dijit/_editor/plugins/FontChoice
22
23
24var _FontDropDown = declare("dijit._editor.plugins._FontDropDown",
25 [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
26 // summary:
27 // Base class for widgets that contains a label (like "Font:")
28 // and a FilteringSelect drop down to pick a value.
29 // Used as Toolbar entry.
30
31 // label: [public] String
32 // The label to apply to this particular FontDropDown.
33 label: "",
34
35 // plainText: [public] boolean
36 // Flag to indicate that the returned label should be plain text
37 // instead of an example.
38 plainText: false,
39
40 // templateString: [public] String
41 // The template used to construct the labeled dropdown.
42 templateString:
43 "<span style='white-space: nowrap' class='dijit dijitReset dijitInline'>" +
44 "<label class='dijitLeft dijitInline' for='${selectId}'>${label}</label>" +
45 "<input data-dojo-type='dijit.form.FilteringSelect' required='false' " +
46 "data-dojo-props='labelType:\"html\", labelAttr:\"label\", searchAttr:\"name\"' " +
47 "tabIndex='-1' id='${selectId}' data-dojo-attach-point='select' value=''/>" +
48 "</span>",
49
50 postMixInProperties: function(){
51 // summary:
52 // Over-ride to set specific properties.
53 this.inherited(arguments);
54
55 this.strings = i18n.getLocalization("dijit._editor", "FontChoice");
56
57 // Set some substitution variables used in the template
58 this.label = this.strings[this.command];
59
60 // _WidgetBase sets the id after postMixInProperties(), but we need it now.
61 // Alternative is to have a buildRendering() method and move this.selectId setting there,
62 // or alternately get rid of selectId variable and just access ${id} in template?
63 this.id = registry.getUniqueId(this.declaredClass.replace(/\./g,"_"));
64
65 this.selectId = this.id + "_select"; // used in template
66
67 this.inherited(arguments);
68 },
69
70 postCreate: function(){
71 // summary:
72 // Over-ride for the default postCreate action
73 // This establishes the filtering selects and the like.
74
75 // Initialize the list of items in the drop down by creating data store with items like:
76 // {value: 1, name: "xx-small", label: "<font size=1>xx-small</font-size>" }
77 this.select.set("store", new MemoryStore({
78 idProperty: "value",
79 data: array.map(this.values, function(value){
80 var name = this.strings[value] || value;
81 return {
82 label: this.getLabel(value, name),
83 name: name,
84 value: value
85 };
86 }, this)
87 }));
88
89 this.select.set("value", "", false);
90 this.disabled = this.select.get("disabled");
91 },
92
93 _setValueAttr: function(value, priorityChange){
94 // summary:
95 // Over-ride for the default action of setting the
96 // widget value, maps the input to known values
97 // value: Object|String
98 // The value to set in the select.
99 // priorityChange:
100 // Optional parameter used to tell the select whether or not to fire
101 // onChange event.
102
103 // if the value is not a permitted value, just set empty string to prevent showing the warning icon
104 priorityChange = priorityChange !== false;
105 this.select.set('value', array.indexOf(this.values,value) < 0 ? "" : value, priorityChange);
106 if(!priorityChange){
107 // Clear the last state in case of updateState calls. Ref: #10466
108 this.select._lastValueReported=null;
109 }
110 },
111
112 _getValueAttr: function(){
113 // summary:
114 // Allow retrieving the value from the composite select on
115 // call to button.get("value");
116 return this.select.get('value');
117 },
118
119 focus: function(){
120 // summary:
121 // Over-ride for focus control of this widget. Delegates focus down to the
122 // filtering select.
123 this.select.focus();
124 },
125
126 _setDisabledAttr: function(value){
127 // summary:
128 // Over-ride for the button's 'disabled' attribute so that it can be
129 // disabled programmatically.
130
131 // Save off ths disabled state so the get retrieves it correctly
132 //without needing to have a function proxy it.
133 this.disabled = value;
134 this.select.set("disabled", value);
135 }
136});
137
138
139var _FontNameDropDown = declare("dijit._editor.plugins._FontNameDropDown", _FontDropDown, {
140 // summary:
141 // Dropdown to select a font; goes in editor toolbar.
142
143 // generic: [const] Boolean
144 // Use generic (web standard) font names
145 generic: false,
146
147 // command: [public] String
148 // The editor 'command' implemented by this plugin.
149 command: "fontName",
150
151 postMixInProperties: function(){
152 // summary:
153 // Over-ride for the default posr mixin control
154 if(!this.values){
155 this.values = this.generic ?
156 ["serif", "sans-serif", "monospace", "cursive", "fantasy"] : // CSS font-family generics
157 ["Arial", "Times New Roman", "Comic Sans MS", "Courier New"];
158 }
159 this.inherited(arguments);
160 },
161
162 getLabel: function(value, name){
163 // summary:
164 // Function used to generate the labels of the format dropdown
165 // will return a formatted, or plain label based on the value
166 // of the plainText option.
167 // value: String
168 // The 'insert value' associated with a name
169 // name: String
170 // The text name of the value
171 if(this.plainText){
172 return name;
173 }else{
174 return "<div style='font-family: "+value+"'>" + name + "</div>";
175 }
176 },
177
178 _setValueAttr: function(value, priorityChange){
179 // summary:
180 // Over-ride for the default action of setting the
181 // widget value, maps the input to known values
182
183 priorityChange = priorityChange !== false;
184 if(this.generic){
185 var map = {
186 "Arial": "sans-serif",
187 "Helvetica": "sans-serif",
188 "Myriad": "sans-serif",
189 "Times": "serif",
190 "Times New Roman": "serif",
191 "Comic Sans MS": "cursive",
192 "Apple Chancery": "cursive",
193 "Courier": "monospace",
194 "Courier New": "monospace",
195 "Papyrus": "fantasy",
196 "Estrangelo Edessa": "cursive", // Windows 7
197 "Gabriola": "fantasy" // Windows 7
198 };
199 value = map[value] || value;
200 }
201 this.inherited(arguments, [value, priorityChange]);
202 }
203});
204
205var _FontSizeDropDown = declare("dijit._editor.plugins._FontSizeDropDown", _FontDropDown, {
206 // summary:
207 // Dropdown to select a font size; goes in editor toolbar.
208
209 // command: [public] String
210 // The editor 'command' implemented by this plugin.
211 command: "fontSize",
212
213 // values: [public] Number[]
214 // The HTML font size values supported by this plugin
215 values: [1,2,3,4,5,6,7], // sizes according to the old HTML FONT SIZE
216
217 getLabel: function(value, name){
218 // summary:
219 // Function used to generate the labels of the format dropdown
220 // will return a formatted, or plain label based on the value
221 // of the plainText option.
222 // We're stuck using the deprecated FONT tag to correspond
223 // with the size measurements used by the editor
224 // value: String
225 // The 'insert value' associated with a name
226 // name: String
227 // The text name of the value
228 if(this.plainText){
229 return name;
230 }else{
231 return "<font size=" + value + "'>" + name + "</font>";
232 }
233 },
234
235 _setValueAttr: function(value, priorityChange){
236 // summary:
237 // Over-ride for the default action of setting the
238 // widget value, maps the input to known values
239 priorityChange = priorityChange !== false;
240 if(value.indexOf && value.indexOf("px") != -1){
241 var pixels = parseInt(value, 10);
242 value = {10:1, 13:2, 16:3, 18:4, 24:5, 32:6, 48:7}[pixels] || value;
243 }
244
245 this.inherited(arguments, [value, priorityChange]);
246 }
247});
248
249
250var _FormatBlockDropDown = declare("dijit._editor.plugins._FormatBlockDropDown", _FontDropDown, {
251 // summary:
252 // Dropdown to select a format (like paragraph or heading); goes in editor toolbar.
253
254 // command: [public] String
255 // The editor 'command' implemented by this plugin.
256 command: "formatBlock",
257
258 // values: [public] Array
259 // The HTML format tags supported by this plugin
260 values: ["noFormat", "p", "h1", "h2", "h3", "pre"],
261
262 postCreate: function(){
263 // Init and set the default value to no formatting. Update state will adjust it
264 // as needed.
265 this.inherited(arguments);
266 this.set("value", "noFormat", false);
267 },
268
269 getLabel: function(value, name){
270 // summary:
271 // Function used to generate the labels of the format dropdown
272 // will return a formatted, or plain label based on the value
273 // of the plainText option.
274 // value: String
275 // The 'insert value' associated with a name
276 // name: String
277 // The text name of the value
278 if(this.plainText || value == "noFormat"){
279 return name;
280 }else{
281 return "<" + value + ">" + name + "</" + value + ">";
282 }
283 },
284
285 _execCommand: function(editor, command, choice){
286 // summary:
287 // Over-ride for default exec-command label.
288 // Allows us to treat 'none' as special.
289 if(choice === "noFormat"){
290 var start;
291 var end;
292 var sel = rangeapi.getSelection(editor.window);
293 if(sel && sel.rangeCount > 0){
294 var range = sel.getRangeAt(0);
295 var node, tag;
296 if(range){
297 start = range.startContainer;
298 end = range.endContainer;
299
300 // find containing nodes of start/end.
301 while(start && start !== editor.editNode &&
302 start !== editor.document.body &&
303 start.nodeType !== 1){
304 start = start.parentNode;
305 }
306
307 while(end && end !== editor.editNode &&
308 end !== editor.document.body &&
309 end.nodeType !== 1){
310 end = end.parentNode;
311 }
312
313 var processChildren = lang.hitch(this, function(node, ary){
314 if(node.childNodes && node.childNodes.length){
315 var i;
316 for(i = 0; i < node.childNodes.length; i++){
317 var c = node.childNodes[i];
318 if(c.nodeType == 1){
319 if(editor._sCall("inSelection", [c])){
320 var tag = c.tagName? c.tagName.toLowerCase(): "";
321 if(array.indexOf(this.values, tag) !== -1){
322 ary.push(c);
323 }
324 processChildren(c, ary);
325 }
326 }
327 }
328 }
329 });
330
331 var unformatNodes = lang.hitch(this, function(nodes){
332 // summary:
333 // Internal function to clear format nodes.
334 // nodes:
335 // The array of nodes to strip formatting from.
336 if(nodes && nodes.length){
337 editor.beginEditing();
338 while(nodes.length){
339 this._removeFormat(editor, nodes.pop());
340 }
341 editor.endEditing();
342 }
343 });
344
345 var clearNodes = [];
346 if(start == end){
347 //Contained within the same block, may be collapsed, but who cares, see if we
348 // have a block element to remove.
349 var block;
350 node = start;
351 while(node && node !== editor.editNode && node !== editor.document.body){
352 if(node.nodeType == 1){
353 tag = node.tagName? node.tagName.toLowerCase(): "";
354 if(array.indexOf(this.values, tag) !== -1){
355 block = node;
356 break;
357 }
358 }
359 node = node.parentNode;
360 }
361
362 //Also look for all child nodes in the selection that may need to be
363 //cleared of formatting
364 processChildren(start, clearNodes);
365 if(block){ clearNodes = [block].concat(clearNodes); }
366 unformatNodes(clearNodes);
367 }else{
368 // Probably a multi select, so we have to process it. Whee.
369 node = start;
370 while(editor._sCall("inSelection", [node])){
371 if(node.nodeType == 1){
372 tag = node.tagName? node.tagName.toLowerCase(): "";
373 if(array.indexOf(this.values, tag) !== -1){
374 clearNodes.push(node);
375 }
376 processChildren(node,clearNodes);
377 }
378 node = node.nextSibling;
379 }
380 unformatNodes(clearNodes);
381 }
382 editor.onDisplayChanged();
383 }
384 }
385 }else{
386 editor.execCommand(command, choice);
387 }
388 },
389
390 _removeFormat: function(editor, node){
391 // summary:
392 // function to remove the block format node.
393 // node:
394 // The block format node to remove (and leave the contents behind)
395 if(editor.customUndo){
396 // So of course IE doesn't work right with paste-overs.
397 // We have to do this manually, which is okay since IE already uses
398 // customUndo and we turned it on for WebKit. WebKit pasted funny,
399 // so couldn't use the execCommand approach
400 while(node.firstChild){
401 domConstruct.place(node.firstChild, node, "before");
402 }
403 node.parentNode.removeChild(node);
404 }else{
405 // Everyone else works fine this way, a paste-over and is native
406 // undo friendly.
407 editor._sCall("selectElementChildren", [node])
408 var html = editor._sCall("getSelectedHtml", [])
409 editor._sCall("selectElement", [node])
410 editor.execCommand("inserthtml", html||"");
411 }
412 }
413});
414
415// TODO: for 2.0, split into FontChoice plugin into three separate classes,
416// one for each command (and change registry below)
417var FontChoice = declare("dijit._editor.plugins.FontChoice", _Plugin,{
418 // summary:
419 // This plugin provides three drop downs for setting style in the editor
420 // (font, font size, and format block), as controlled by command.
421 //
422 // description:
423 // The commands provided by this plugin are:
424 //
425 // - fontName: Provides a drop down to select from a list of font names
426 // - fontSize: Provides a drop down to select from a list of font sizes
427 // - formatBlock: Provides a drop down to select from a list of block styles
428 // which can easily be added to an editor by including one or more of the above commands
429 // in the `plugins` attribute as follows:
430 //
431 // | plugins="['fontName','fontSize',...]"
432 //
433 // It is possible to override the default dropdown list by providing an Array for the `custom` property when
434 // instantiating this plugin, e.g.
435 //
436 // | plugins="[{name:'dijit._editor.plugins.FontChoice', command:'fontName', values:['Verdana','Myriad','Garamond']},...]"
437 //
438 // Alternatively, for `fontName` only, `generic:true` may be specified to provide a dropdown with
439 // [CSS generic font families](http://www.w3.org/TR/REC-CSS2/fonts.html#generic-font-families).
440 //
441 // Note that the editor is often unable to properly handle font styling information defined outside
442 // the context of the current editor instance, such as pre-populated HTML.
443
444 // useDefaultCommand: [protected] Boolean
445 // Override _Plugin.useDefaultCommand...
446 // processing is handled by this plugin, not by dijit/Editor.
447 useDefaultCommand: false,
448
449 _initButton: function(){
450 // summary:
451 // Overrides _Plugin._initButton(), to initialize the FilteringSelect+label in toolbar,
452 // rather than a simple button.
453 // tags:
454 // protected
455
456 // Create the widget to go into the toolbar (the so-called "button")
457 var clazz = {
458 fontName: _FontNameDropDown,
459 fontSize: _FontSizeDropDown,
460 formatBlock: _FormatBlockDropDown
461 }[this.command],
462 params = this.params;
463
464 // For back-compat reasons support setting custom values via "custom" parameter
465 // rather than "values" parameter. Remove in 2.0.
466 if(this.params.custom){
467 params.values = this.params.custom;
468 }
469
470 var editor = this.editor;
471 this.button = new clazz(lang.delegate({dir: editor.dir, lang: editor.lang}, params));
472
473 // Reflect changes to the drop down in the editor
474 this.connect(this.button.select, "onChange", function(choice){
475 // User invoked change, since all internal updates set priorityChange to false and will
476 // not trigger an onChange event.
477 this.editor.focus();
478
479 if(this.command == "fontName" && choice.indexOf(" ") != -1){ choice = "'" + choice + "'"; }
480
481 // Invoke, the editor already normalizes commands called through its
482 // execCommand.
483 if(this.button._execCommand){
484 this.button._execCommand(this.editor, this.command, choice);
485 }else{
486 this.editor.execCommand(this.command, choice);
487 }
488 });
489 },
490
491 updateState: function(){
492 // summary:
493 // Overrides _Plugin.updateState(). This controls updating the menu
494 // options to the right values on state changes in the document (that trigger a
495 // test of the actions.)
496 // It set value of drop down in toolbar to reflect font/font size/format block
497 // of text at current caret position.
498 // tags:
499 // protected
500 var _e = this.editor;
501 var _c = this.command;
502 if(!_e || !_e.isLoaded || !_c.length){ return; }
503
504 if(this.button){
505 var disabled = this.get("disabled");
506 this.button.set("disabled", disabled);
507 if(disabled){ return; }
508 var value;
509 try{
510 value = _e.queryCommandValue(_c) || "";
511 }catch(e){
512 //Firefox may throw error above if the editor is just loaded, ignore it
513 value = "";
514 }
515
516 // strip off single quotes, if any
517 var quoted = lang.isString(value) && value.match(/'([^']*)'/);
518 if(quoted){ value = quoted[1]; }
519
520 if(_c === "formatBlock"){
521 if(!value || value == "p"){
522 // Some browsers (WebKit) doesn't actually get the tag info right.
523 // and IE returns paragraph when in a DIV!, so incorrect a lot,
524 // so we have double-check it.
525 value = null;
526 var elem;
527 // Try to find the current element where the caret is.
528 var sel = rangeapi.getSelection(this.editor.window);
529 if(sel && sel.rangeCount > 0){
530 var range = sel.getRangeAt(0);
531 if(range){
532 elem = range.endContainer;
533 }
534 }
535
536 // Okay, now see if we can find one of the formatting types we're in.
537 while(elem && elem !== _e.editNode && elem !== _e.document){
538 var tg = elem.tagName?elem.tagName.toLowerCase():"";
539 if(tg && array.indexOf(this.button.values, tg) > -1){
540 value = tg;
541 break;
542 }
543 elem = elem.parentNode;
544 }
545 if(!value){
546 // Still no value, so lets select 'none'.
547 value = "noFormat";
548 }
549 }else{
550 // Check that the block format is one allowed, if not,
551 // null it so that it gets set to empty.
552 if(array.indexOf(this.button.values, value) < 0){
553 value = "noFormat";
554 }
555 }
556 }
557 if(value !== this.button.get("value")){
558 // Set the value, but denote it is not a priority change, so no
559 // onchange fires.
560 this.button.set('value', value, false);
561 }
562 }
563 }
564});
565
566// Register these plugins
567array.forEach(["fontName", "fontSize", "formatBlock"], function(name){
568 _Plugin.registry[name] = function(args){
569 return new FontChoice({
570 command: name,
571 plainText: args.plainText
572 });
573 };
574});
575
576// Make all classes available through AMD, and return main class
577FontChoice._FontDropDown = _FontDropDown;
578FontChoice._FontNameDropDown = _FontNameDropDown;
579FontChoice._FontSizeDropDown = _FontSizeDropDown;
580FontChoice._FormatBlockDropDown = _FormatBlockDropDown;
581return FontChoice;
582
583});