]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/_editor/plugins/FontChoice.js.uncompressed.js
1 define("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
10 "../../_TemplatedMixin",
11 "../../_WidgetsInTemplateMixin",
12 "../../form/FilteringSelect",
15 "dojo/i18n!../nls/FontChoice"
16 ], function(array
, declare
, domConstruct
, i18n
, lang
, MemoryStore
,
17 registry
, _Widget
, _TemplatedMixin
, _WidgetsInTemplateMixin
, FilteringSelect
, _Plugin
, rangeapi
){
21 // dijit/_editor/plugins/FontChoice
24 var _FontDropDown
= declare("dijit._editor.plugins._FontDropDown",
25 [_Widget
, _TemplatedMixin
, _WidgetsInTemplateMixin
], {
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.
31 // label: [public] String
32 // The label to apply to this particular FontDropDown.
35 // plainText: [public] boolean
36 // Flag to indicate that the returned label should be plain text
37 // instead of an example.
40 // templateString: [public] String
41 // The template used to construct the labeled dropdown.
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=''/>" +
50 postMixInProperties: function(){
52 // Over-ride to set specific properties.
53 this.inherited(arguments
);
55 this.strings
= i18n
.getLocalization("dijit._editor", "FontChoice");
57 // Set some substitution variables used in the template
58 this.label
= this.strings
[this.command
];
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,"_"));
65 this.selectId
= this.id
+ "_select"; // used in template
67 this.inherited(arguments
);
70 postCreate: function(){
72 // Over-ride for the default postCreate action
73 // This establishes the filtering selects and the like.
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({
79 data
: array
.map(this.values
, function(value
){
80 var name
= this.strings
[value
] || value
;
82 label
: this.getLabel(value
, name
),
89 this.select
.set("value", "", false);
90 this.disabled
= this.select
.get("disabled");
93 _setValueAttr: function(value
, priorityChange
){
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.
100 // Optional parameter used to tell the select whether or not to fire
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
);
107 // Clear the last state in case of updateState calls. Ref: #10466
108 this.select
._lastValueReported
=null;
112 _getValueAttr: function(){
114 // Allow retrieving the value from the composite select on
115 // call to button.get("value");
116 return this.select
.get('value');
121 // Over-ride for focus control of this widget. Delegates focus down to the
126 _setDisabledAttr: function(value
){
128 // Over-ride for the button's 'disabled' attribute so that it can be
129 // disabled programmatically.
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
);
139 var _FontNameDropDown
= declare("dijit._editor.plugins._FontNameDropDown", _FontDropDown
, {
141 // Dropdown to select a font; goes in editor toolbar.
143 // generic: [const] Boolean
144 // Use generic (web standard) font names
147 // command: [public] String
148 // The editor 'command' implemented by this plugin.
151 postMixInProperties: function(){
153 // Over-ride for the default posr mixin control
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"];
159 this.inherited(arguments
);
162 getLabel: function(value
, name
){
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.
168 // The 'insert value' associated with a name
170 // The text name of the value
174 return "<div style='font-family: "+value
+"'>" + name
+ "</div>";
178 _setValueAttr: function(value
, priorityChange
){
180 // Over-ride for the default action of setting the
181 // widget value, maps the input to known values
183 priorityChange
= priorityChange
!== false;
186 "Arial": "sans-serif",
187 "Helvetica": "sans-serif",
188 "Myriad": "sans-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
199 value
= map
[value
] || value
;
201 this.inherited(arguments
, [value
, priorityChange
]);
205 var _FontSizeDropDown
= declare("dijit._editor.plugins._FontSizeDropDown", _FontDropDown
, {
207 // Dropdown to select a font size; goes in editor toolbar.
209 // command: [public] String
210 // The editor 'command' implemented by this plugin.
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
217 getLabel: function(value
, name
){
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
225 // The 'insert value' associated with a name
227 // The text name of the value
231 return "<font size=" + value
+ "'>" + name
+ "</font>";
235 _setValueAttr: function(value
, priorityChange
){
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
;
245 this.inherited(arguments
, [value
, priorityChange
]);
250 var _FormatBlockDropDown
= declare("dijit._editor.plugins._FormatBlockDropDown", _FontDropDown
, {
252 // Dropdown to select a format (like paragraph or heading); goes in editor toolbar.
254 // command: [public] String
255 // The editor 'command' implemented by this plugin.
256 command
: "formatBlock",
258 // values: [public] Array
259 // The HTML format tags supported by this plugin
260 values
: ["noFormat", "p", "h1", "h2", "h3", "pre"],
262 postCreate: function(){
263 // Init and set the default value to no formatting. Update state will adjust it
265 this.inherited(arguments
);
266 this.set("value", "noFormat", false);
269 getLabel: function(value
, name
){
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.
275 // The 'insert value' associated with a name
277 // The text name of the value
278 if(this.plainText
|| value
== "noFormat"){
281 return "<" + value
+ ">" + name
+ "</" + value
+ ">";
285 _execCommand: function(editor
, command
, choice
){
287 // Over-ride for default exec-command label.
288 // Allows us to treat 'none' as special.
289 if(choice
=== "noFormat"){
292 var sel
= rangeapi
.getSelection(editor
.window
);
293 if(sel
&& sel
.rangeCount
> 0){
294 var range
= sel
.getRangeAt(0);
297 start
= range
.startContainer
;
298 end
= range
.endContainer
;
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
;
307 while(end
&& end
!== editor
.editNode
&&
308 end
!== editor
.document
.body
&&
310 end
= end
.parentNode
;
313 var processChildren
= lang
.hitch(this, function(node
, ary
){
314 if(node
.childNodes
&& node
.childNodes
.length
){
316 for(i
= 0; i
< node
.childNodes
.length
; i
++){
317 var c
= node
.childNodes
[i
];
319 if(editor
._sCall("inSelection", [c
])){
320 var tag
= c
.tagName
? c
.tagName
.toLowerCase(): "";
321 if(array
.indexOf(this.values
, tag
) !== -1){
324 processChildren(c
, ary
);
331 var unformatNodes
= lang
.hitch(this, function(nodes
){
333 // Internal function to clear format nodes.
335 // The array of nodes to strip formatting from.
336 if(nodes
&& nodes
.length
){
337 editor
.beginEditing();
339 this._removeFormat(editor
, nodes
.pop());
347 //Contained within the same block, may be collapsed, but who cares, see if we
348 // have a block element to remove.
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){
359 node
= node
.parentNode
;
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
);
368 // Probably a multi select, so we have to process it. Whee.
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
);
376 processChildren(node
,clearNodes
);
378 node
= node
.nextSibling
;
380 unformatNodes(clearNodes
);
382 editor
.onDisplayChanged();
386 editor
.execCommand(command
, choice
);
390 _removeFormat: function(editor
, node
){
392 // function to remove the block format 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");
403 node
.parentNode
.removeChild(node
);
405 // Everyone else works fine this way, a paste-over and is native
407 editor
._sCall("selectElementChildren", [node
])
408 var html
= editor
._sCall("getSelectedHtml", [])
409 editor
._sCall("selectElement", [node
])
410 editor
.execCommand("inserthtml", html
||"");
415 // TODO: for 2.0, split into FontChoice plugin into three separate classes,
416 // one for each command (and change registry below)
417 var FontChoice
= declare("dijit._editor.plugins.FontChoice", _Plugin
,{
419 // This plugin provides three drop downs for setting style in the editor
420 // (font, font size, and format block), as controlled by command.
423 // The commands provided by this plugin are:
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:
431 // | plugins="['fontName','fontSize',...]"
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.
436 // | plugins="[{name:'dijit._editor.plugins.FontChoice', command:'fontName', values:['Verdana','Myriad','Garamond']},...]"
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).
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.
444 // useDefaultCommand: [protected] Boolean
445 // Override _Plugin.useDefaultCommand...
446 // processing is handled by this plugin, not by dijit/Editor.
447 useDefaultCommand
: false,
449 _initButton: function(){
451 // Overrides _Plugin._initButton(), to initialize the FilteringSelect+label in toolbar,
452 // rather than a simple button.
456 // Create the widget to go into the toolbar (the so-called "button")
458 fontName
: _FontNameDropDown
,
459 fontSize
: _FontSizeDropDown
,
460 formatBlock
: _FormatBlockDropDown
462 params
= this.params
;
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
;
470 var editor
= this.editor
;
471 this.button
= new clazz(lang
.delegate({dir
: editor
.dir
, lang
: editor
.lang
}, params
));
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.
479 if(this.command
== "fontName" && choice
.indexOf(" ") != -1){ choice
= "'" + choice
+ "'"; }
481 // Invoke, the editor already normalizes commands called through its
483 if(this.button
._execCommand
){
484 this.button
._execCommand(this.editor
, this.command
, choice
);
486 this.editor
.execCommand(this.command
, choice
);
491 updateState: function(){
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.
500 var _e
= this.editor
;
501 var _c
= this.command
;
502 if(!_e
|| !_e
.isLoaded
|| !_c
.length
){ return; }
505 var disabled
= this.get("disabled");
506 this.button
.set("disabled", disabled
);
507 if(disabled
){ return; }
510 value
= _e
.queryCommandValue(_c
) || "";
512 //Firefox may throw error above if the editor is just loaded, ignore it
516 // strip off single quotes, if any
517 var quoted
= lang
.isString(value
) && value
.match(/'([^']*)'/);
518 if(quoted
){ value
= quoted
[1]; }
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.
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);
532 elem
= range
.endContainer
;
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){
543 elem
= elem
.parentNode
;
546 // Still no value, so lets select 'none'.
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){
557 if(value
!== this.button
.get("value")){
558 // Set the value, but denote it is not a priority change, so no
560 this.button
.set('value', value
, false);
566 // Register these plugins
567 array
.forEach(["fontName", "fontSize", "formatBlock"], function(name
){
568 _Plugin
.registry
[name
] = function(args
){
569 return new FontChoice({
571 plainText
: args
.plainText
576 // Make all classes available through AMD, and return main class
577 FontChoice
._FontDropDown
= _FontDropDown
;
578 FontChoice
._FontNameDropDown
= _FontNameDropDown
;
579 FontChoice
._FontSizeDropDown
= _FontSizeDropDown
;
580 FontChoice
._FormatBlockDropDown
= _FormatBlockDropDown
;