diff options
Diffstat (limited to 'lib/dijit/_editor/_Plugin.js')
-rw-r--r-- | lib/dijit/_editor/_Plugin.js | 342 |
1 files changed, 279 insertions, 63 deletions
diff --git a/lib/dijit/_editor/_Plugin.js b/lib/dijit/_editor/_Plugin.js index f3c95aae0..2f769960d 100644 --- a/lib/dijit/_editor/_Plugin.js +++ b/lib/dijit/_editor/_Plugin.js @@ -1,73 +1,289 @@ /* - Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved. + Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ -if(!dojo._hasResource["dijit._editor._Plugin"]){ -dojo._hasResource["dijit._editor._Plugin"]=true; +if(!dojo._hasResource["dijit._editor._Plugin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._editor._Plugin"] = true; dojo.provide("dijit._editor._Plugin"); dojo.require("dijit._Widget"); dojo.require("dijit.form.Button"); -dojo.declare("dijit._editor._Plugin",null,{constructor:function(_1,_2){ -this.params=_1||{}; -dojo.mixin(this,this.params); -this._connects=[]; -},editor:null,iconClassPrefix:"dijitEditorIcon",button:null,command:"",useDefaultCommand:true,buttonClass:dijit.form.Button,getLabel:function(_3){ -return this.editor.commands[_3]; -},_initButton:function(){ -if(this.command.length){ -var _4=this.getLabel(this.command),_5=this.editor,_6=this.iconClassPrefix+" "+this.iconClassPrefix+this.command.charAt(0).toUpperCase()+this.command.substr(1); -if(!this.button){ -var _7=dojo.mixin({label:_4,dir:_5.dir,lang:_5.lang,showLabel:false,iconClass:_6,dropDown:this.dropDown,tabIndex:"-1"},this.params||{}); -this.button=new this.buttonClass(_7); -} -} -},destroy:function(){ -dojo.forEach(this._connects,dojo.disconnect); -if(this.dropDown){ -this.dropDown.destroyRecursive(); -} -},connect:function(o,f,tf){ -this._connects.push(dojo.connect(o,f,this,tf)); -},updateState:function(){ -var e=this.editor,c=this.command,_8,_9; -if(!e||!e.isLoaded||!c.length){ -return; -} -if(this.button){ -try{ -_9=e.queryCommandEnabled(c); -if(this.enabled!==_9){ -this.enabled=_9; -this.button.set("disabled",!_9); -} -if(typeof this.button.checked=="boolean"){ -_8=e.queryCommandState(c); -if(this.checked!==_8){ -this.checked=_8; -this.button.set("checked",e.queryCommandState(c)); -} -} -} -catch(e){ -} -} -},setEditor:function(_a){ -this.editor=_a; -this._initButton(); -if(this.button&&this.useDefaultCommand){ -if(this.editor.queryCommandAvailable(this.command)){ -this.connect(this.button,"onClick",dojo.hitch(this.editor,"execCommand",this.command,this.commandArg)); -}else{ -this.button.domNode.style.display="none"; -} -} -this.connect(this.editor,"onNormalizedDisplayChanged","updateState"); -},setToolbar:function(_b){ -if(this.button){ -_b.addChild(this.button); -} -}}); + + +dojo.declare("dijit._editor._Plugin", null, { + // summary + // Base class for a "plugin" to the editor, which is usually + // a single button on the Toolbar and some associated code + + constructor: function(/*Object?*/args, /*DomNode?*/node){ + this.params = args || {}; + dojo.mixin(this, this.params); + this._connects=[]; + this._attrPairNames = {}; + }, + + // editor: [const] dijit.Editor + // Points to the parent editor + editor: null, + + // iconClassPrefix: [const] String + // The CSS class name for the button node is formed from `iconClassPrefix` and `command` + iconClassPrefix: "dijitEditorIcon", + + // button: dijit._Widget? + // Pointer to `dijit.form.Button` or other widget (ex: `dijit.form.FilteringSelect`) + // that is added to the toolbar to control this plugin. + // If not specified, will be created on initialization according to `buttonClass` + button: null, + + // command: String + // String like "insertUnorderedList", "outdent", "justifyCenter", etc. that represents an editor command. + // Passed to editor.execCommand() if `useDefaultCommand` is true. + command: "", + + // useDefaultCommand: Boolean + // If true, this plugin executes by calling Editor.execCommand() with the argument specified in `command`. + useDefaultCommand: true, + + // buttonClass: Widget Class + // Class of widget (ex: dijit.form.Button or dijit.form.FilteringSelect) + // that is added to the toolbar to control this plugin. + // This is used to instantiate the button, unless `button` itself is specified directly. + buttonClass: dijit.form.Button, + + // disabled: Boolean + // Flag to indicate if this plugin has been disabled and should do nothing + // helps control button state, among other things. Set via the setter api. + disabled: false, + + getLabel: function(/*String*/key){ + // summary: + // Returns the label to use for the button + // tags: + // private + return this.editor.commands[key]; // String + }, + + _initButton: function(){ + // summary: + // Initialize the button or other widget that will control this plugin. + // This code only works for plugins controlling built-in commands in the editor. + // tags: + // protected extension + if(this.command.length){ + var label = this.getLabel(this.command), + editor = this.editor, + className = this.iconClassPrefix+" "+this.iconClassPrefix + this.command.charAt(0).toUpperCase() + this.command.substr(1); + if(!this.button){ + var props = dojo.mixin({ + label: label, + dir: editor.dir, + lang: editor.lang, + showLabel: false, + iconClass: className, + dropDown: this.dropDown, + tabIndex: "-1" + }, this.params || {}); + this.button = new this.buttonClass(props); + } + } + if(this.get("disabled") && this.button){ + this.button.set("disabled", this.get("disabled")); + } + }, + + destroy: function(){ + // summary: + // Destroy this plugin + + dojo.forEach(this._connects, dojo.disconnect); + if(this.dropDown){ + this.dropDown.destroyRecursive(); + } + }, + + connect: function(o, f, tf){ + // summary: + // Make a dojo.connect() that is automatically disconnected when this plugin is destroyed. + // Similar to `dijit._Widget.connect`. + // tags: + // protected + this._connects.push(dojo.connect(o, f, this, tf)); + }, + + updateState: function(){ + // summary: + // Change state of the plugin to respond to events in the editor. + // description: + // This is called on meaningful events in the editor, such as change of selection + // or caret position (but not simple typing of alphanumeric keys). It gives the + // plugin a chance to update the CSS of its button. + // + // For example, the "bold" plugin will highlight/unhighlight the bold button depending on whether the + // characters next to the caret are bold or not. + // + // Only makes sense when `useDefaultCommand` is true, as it calls Editor.queryCommandEnabled(`command`). + var e = this.editor, + c = this.command, + checked, enabled; + if(!e || !e.isLoaded || !c.length){ return; } + var disabled = this.get("disabled"); + if(this.button){ + try{ + enabled = !disabled && e.queryCommandEnabled(c); + if(this.enabled !== enabled){ + this.enabled = enabled; + this.button.set('disabled', !enabled); + } + if(typeof this.button.checked == 'boolean'){ + checked = e.queryCommandState(c); + if(this.checked !== checked){ + this.checked = checked; + this.button.set('checked', e.queryCommandState(c)); + } + } + }catch(e){ + console.log(e); // FIXME: we shouldn't have debug statements in our code. Log as an error? + } + } + }, + + setEditor: function(/*dijit.Editor*/ editor){ + // summary: + // Tell the plugin which Editor it is associated with. + + // TODO: refactor code to just pass editor to constructor. + + // FIXME: detach from previous editor!! + this.editor = editor; + + // FIXME: prevent creating this if we don't need to (i.e., editor can't handle our command) + this._initButton(); + + // Processing for buttons that execute by calling editor.execCommand() + if(this.button && this.useDefaultCommand){ + if(this.editor.queryCommandAvailable(this.command)){ + this.connect(this.button, "onClick", + dojo.hitch(this.editor, "execCommand", this.command, this.commandArg) + ); + }else{ + // hide button because editor doesn't support command (due to browser limitations) + this.button.domNode.style.display = "none"; + } + } + + this.connect(this.editor, "onNormalizedDisplayChanged", "updateState"); + }, + + setToolbar: function(/*dijit.Toolbar*/ toolbar){ + // summary: + // Tell the plugin to add it's controller widget (often a button) + // to the toolbar. Does nothing if there is no controller widget. + + // TODO: refactor code to just pass toolbar to constructor. + + if(this.button){ + toolbar.addChild(this.button); + } + // console.debug("adding", this.button, "to:", toolbar); + }, + + set: function(/* attribute */ name, /* anything */ value){ + // summary: + // Set a property on a plugin + // name: + // The property to set. + // value: + // The value to set in the property. + // description: + // Sets named properties on a plugin which may potentially be handled by a + // setter in the plugin. + // For example, if the plugin has a properties "foo" + // and "bar" and a method named "_setFooAttr", calling: + // | plugin.set("foo", "Howdy!"); + // would be equivalent to writing: + // | plugin._setFooAttr("Howdy!"); + // and: + // | plugin.set("bar", 3); + // would be equivalent to writing: + // | plugin.bar = 3; + // + // set() may also be called with a hash of name/value pairs, ex: + // | plugin.set({ + // | foo: "Howdy", + // | bar: 3 + // | }) + // This is equivalent to calling set(foo, "Howdy") and set(bar, 3) + if(typeof name === "object"){ + for(var x in name){ + this.set(x, name[x]); + } + return this; + } + var names = this._getAttrNames(name); + if(this[names.s]){ + // use the explicit setter + var result = this[names.s].apply(this, Array.prototype.slice.call(arguments, 1)); + }else{ + this._set(name, value); + } + return result || this; + }, + + get: function(name){ + // summary: + // Get a property from a plugin. + // name: + // The property to get. + // description: + // Get a named property from a plugin. The property may + // potentially be retrieved via a getter method. If no getter is defined, this + // just retrieves the object's property. + // For example, if the plugin has a properties "foo" + // and "bar" and a method named "_getFooAttr", calling: + // | plugin.get("foo"); + // would be equivalent to writing: + // | plugin._getFooAttr(); + // and: + // | plugin.get("bar"); + // would be equivalent to writing: + // | plugin.bar; + var names = this._getAttrNames(name); + return this[names.g] ? this[names.g]() : this[name]; + }, + + _setDisabledAttr: function(disabled){ + // summary: + // Function to set the plugin state and call updateState to make sure the + // button is updated appropriately. + this.disabled = disabled; + this.updateState(); + }, + + _getAttrNames: function(name){ + // summary: + // Helper function for get() and set(). + // Caches attribute name values so we don't do the string ops every time. + // tags: + // private + + var apn = this._attrPairNames; + if(apn[name]){ return apn[name]; } + var uc = name.charAt(0).toUpperCase() + name.substr(1); + return (apn[name] = { + s: "_set"+uc+"Attr", + g: "_get"+uc+"Attr" + }); + }, + + _set: function(/*String*/ name, /*anything*/ value){ + // summary: + // Helper function to set new value for specified attribute + var oldValue = this[name]; + this[name] = value; + } +}); + } |