_Plugin.js 8.8 KB

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