]> git.wh0rd.org - tt-rss.git/blob - lib/dijit/_editor/_Plugin.js.uncompressed.js
upgrade dojo to 1.8.3 (refs #570)
[tt-rss.git] / lib / dijit / _editor / _Plugin.js.uncompressed.js
1 define("dijit/_editor/_Plugin", [
2 "dojo/_base/connect", // connect.connect
3 "dojo/_base/declare", // declare
4 "dojo/_base/lang", // lang.mixin, lang.hitch
5 "../form/Button"
6 ], function(connect, declare, lang, Button){
7
8 // module:
9 // dijit/_editor/_Plugin
10 // summary:
11 // Base class for a "plugin" to the editor, which is usually
12 // a single button on the Toolbar and some associated code
13
14
15 var _Plugin = declare("dijit._editor._Plugin", null, {
16 // summary:
17 // Base class for a "plugin" to the editor, which is usually
18 // a single button on the Toolbar and some associated code
19
20 constructor: function(args){
21 // summary:
22 // Create the plugin.
23 // args: Object?
24 // Initial settings for any of the attributes.
25
26 this.params = args || {};
27 lang.mixin(this, this.params);
28 this._connects=[];
29 this._attrPairNames = {};
30 },
31
32 // editor: [const] dijit.Editor
33 // Points to the parent editor
34 editor: null,
35
36 // iconClassPrefix: [const] String
37 // The CSS class name for the button node is formed from `iconClassPrefix` and `command`
38 iconClassPrefix: "dijitEditorIcon",
39
40 // button: dijit/_WidgetBase?
41 // Pointer to `dijit/form/Button` or other widget (ex: `dijit/form/FilteringSelect`)
42 // that is added to the toolbar to control this plugin.
43 // If not specified, will be created on initialization according to `buttonClass`
44 button: null,
45
46 // command: String
47 // String like "insertUnorderedList", "outdent", "justifyCenter", etc. that represents an editor command.
48 // Passed to editor.execCommand() if `useDefaultCommand` is true.
49 command: "",
50
51 // useDefaultCommand: Boolean
52 // If true, this plugin executes by calling Editor.execCommand() with the argument specified in `command`.
53 useDefaultCommand: true,
54
55 // buttonClass: Widget Class
56 // Class of widget (ex: dijit.form.Button or dijit/form/FilteringSelect)
57 // that is added to the toolbar to control this plugin.
58 // This is used to instantiate the button, unless `button` itself is specified directly.
59 buttonClass: Button,
60
61 // disabled: Boolean
62 // Flag to indicate if this plugin has been disabled and should do nothing
63 // helps control button state, among other things. Set via the setter api.
64 disabled: false,
65
66 getLabel: function(/*String*/key){
67 // summary:
68 // Returns the label to use for the button
69 // tags:
70 // private
71 return this.editor.commands[key]; // String
72 },
73
74 _initButton: function(){
75 // summary:
76 // Initialize the button or other widget that will control this plugin.
77 // This code only works for plugins controlling built-in commands in the editor.
78 // tags:
79 // protected extension
80 if(this.command.length){
81 var label = this.getLabel(this.command),
82 editor = this.editor,
83 className = this.iconClassPrefix+" "+this.iconClassPrefix + this.command.charAt(0).toUpperCase() + this.command.substr(1);
84 if(!this.button){
85 var props = lang.mixin({
86 label: label,
87 ownerDocument: editor.ownerDocument,
88 dir: editor.dir,
89 lang: editor.lang,
90 showLabel: false,
91 iconClass: className,
92 dropDown: this.dropDown,
93 tabIndex: "-1"
94 }, this.params || {});
95 this.button = new this.buttonClass(props);
96 }
97 }
98 if(this.get("disabled") && this.button){
99 this.button.set("disabled", this.get("disabled"));
100 }
101 },
102
103 destroy: function(){
104 // summary:
105 // Destroy this plugin
106
107 var h;
108 while(h = this._connects.pop()){ h.remove(); }
109 if(this.dropDown){
110 this.dropDown.destroyRecursive();
111 }
112 },
113
114 connect: function(o, f, tf){
115 // summary:
116 // Make a connect.connect() that is automatically disconnected when this plugin is destroyed.
117 // Similar to `dijit/_Widget.connect()`.
118 // tags:
119 // protected
120 this._connects.push(connect.connect(o, f, this, tf));
121 },
122
123 updateState: function(){
124 // summary:
125 // Change state of the plugin to respond to events in the editor.
126 // description:
127 // This is called on meaningful events in the editor, such as change of selection
128 // or caret position (but not simple typing of alphanumeric keys). It gives the
129 // plugin a chance to update the CSS of its button.
130 //
131 // For example, the "bold" plugin will highlight/unhighlight the bold button depending on whether the
132 // characters next to the caret are bold or not.
133 //
134 // Only makes sense when `useDefaultCommand` is true, as it calls Editor.queryCommandEnabled(`command`).
135 var e = this.editor,
136 c = this.command,
137 checked, enabled;
138 if(!e || !e.isLoaded || !c.length){ return; }
139 var disabled = this.get("disabled");
140 if(this.button){
141 try{
142 enabled = !disabled && e.queryCommandEnabled(c);
143 if(this.enabled !== enabled){
144 this.enabled = enabled;
145 this.button.set('disabled', !enabled);
146 }
147 if(enabled){
148 if(typeof this.button.checked == 'boolean'){
149 checked = e.queryCommandState(c);
150 if(this.checked !== checked){
151 this.checked = checked;
152 this.button.set('checked', e.queryCommandState(c));
153 }
154 }
155 }
156 }catch(e){
157 console.log(e); // FIXME: we shouldn't have debug statements in our code. Log as an error?
158 }
159 }
160 },
161
162 setEditor: function(/*dijit/Editor*/ editor){
163 // summary:
164 // Tell the plugin which Editor it is associated with.
165
166 // TODO: refactor code to just pass editor to constructor.
167
168 // FIXME: detach from previous editor!!
169 this.editor = editor;
170
171 // FIXME: prevent creating this if we don't need to (i.e., editor can't handle our command)
172 this._initButton();
173
174 // Processing for buttons that execute by calling editor.execCommand()
175 if(this.button && this.useDefaultCommand){
176 if(this.editor.queryCommandAvailable(this.command)){
177 this.connect(this.button, "onClick",
178 lang.hitch(this.editor, "execCommand", this.command, this.commandArg)
179 );
180 }else{
181 // hide button because editor doesn't support command (due to browser limitations)
182 this.button.domNode.style.display = "none";
183 }
184 }
185
186 this.connect(this.editor, "onNormalizedDisplayChanged", "updateState");
187 },
188
189 setToolbar: function(/*dijit/Toolbar*/ toolbar){
190 // summary:
191 // Tell the plugin to add it's controller widget (often a button)
192 // to the toolbar. Does nothing if there is no controller widget.
193
194 // TODO: refactor code to just pass toolbar to constructor.
195
196 if(this.button){
197 toolbar.addChild(this.button);
198 }
199 // console.debug("adding", this.button, "to:", toolbar);
200 },
201
202 set: function(/* attribute */ name, /* anything */ value){
203 // summary:
204 // Set a property on a plugin
205 // name:
206 // The property to set.
207 // value:
208 // The value to set in the property.
209 // description:
210 // Sets named properties on a plugin which may potentially be handled by a
211 // setter in the plugin.
212 // For example, if the plugin has a properties "foo"
213 // and "bar" and a method named "_setFooAttr", calling:
214 // | plugin.set("foo", "Howdy!");
215 // would be equivalent to writing:
216 // | plugin._setFooAttr("Howdy!");
217 // and:
218 // | plugin.set("bar", 3);
219 // would be equivalent to writing:
220 // | plugin.bar = 3;
221 //
222 // set() may also be called with a hash of name/value pairs, ex:
223 // | plugin.set({
224 // | foo: "Howdy",
225 // | bar: 3
226 // | })
227 // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
228 if(typeof name === "object"){
229 for(var x in name){
230 this.set(x, name[x]);
231 }
232 return this;
233 }
234 var names = this._getAttrNames(name);
235 if(this[names.s]){
236 // use the explicit setter
237 var result = this[names.s].apply(this, Array.prototype.slice.call(arguments, 1));
238 }else{
239 this._set(name, value);
240 }
241 return result || this;
242 },
243
244 get: function(name){
245 // summary:
246 // Get a property from a plugin.
247 // name:
248 // The property to get.
249 // description:
250 // Get a named property from a plugin. The property may
251 // potentially be retrieved via a getter method. If no getter is defined, this
252 // just retrieves the object's property.
253 // For example, if the plugin has a properties "foo"
254 // and "bar" and a method named "_getFooAttr", calling:
255 // | plugin.get("foo");
256 // would be equivalent to writing:
257 // | plugin._getFooAttr();
258 // and:
259 // | plugin.get("bar");
260 // would be equivalent to writing:
261 // | plugin.bar;
262 var names = this._getAttrNames(name);
263 return this[names.g] ? this[names.g]() : this[name];
264 },
265
266 _setDisabledAttr: function(disabled){
267 // summary:
268 // Function to set the plugin state and call updateState to make sure the
269 // button is updated appropriately.
270 this.disabled = disabled;
271 this.updateState();
272 },
273
274 _getAttrNames: function(name){
275 // summary:
276 // Helper function for get() and set().
277 // Caches attribute name values so we don't do the string ops every time.
278 // tags:
279 // private
280
281 var apn = this._attrPairNames;
282 if(apn[name]){ return apn[name]; }
283 var uc = name.charAt(0).toUpperCase() + name.substr(1);
284 return (apn[name] = {
285 s: "_set"+uc+"Attr",
286 g: "_get"+uc+"Attr"
287 });
288 },
289
290 _set: function(/*String*/ name, /*anything*/ value){
291 // summary:
292 // Helper function to set new value for specified attribute
293 this[name] = value;
294 }
295 });
296
297 // Hash mapping plugin name to factory, used for registering plugins
298 _Plugin.registry = {};
299
300 return _Plugin;
301
302 });