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