Breadcrumb.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. define("dojox/editor/plugins/Breadcrumb", [
  2. "dojo",
  3. "dijit",
  4. "dojox",
  5. "dijit/_Widget",
  6. "dijit/_TemplatedMixin",
  7. "dijit/Toolbar",
  8. "dijit/Menu",
  9. "dijit/MenuItem",
  10. "dijit/MenuSeparator",
  11. "dijit/_editor/range",
  12. "dijit/_editor/selection",
  13. "dijit/_editor/_Plugin",
  14. "dijit/form/Button",
  15. "dijit/form/ComboButton",
  16. "dojo/_base/connect",
  17. "dojo/_base/declare",
  18. "dojo/i18n",
  19. "dojo/string",
  20. "dojo/i18n!dojox/editor/plugins/nls/Breadcrumb"
  21. ], function(dojo, dijit, dojox) {
  22. dojo.experimental("dojox.editor.plugins.Breadcrumb");
  23. dojo.declare("dojox.editor.plugins._BreadcrumbMenuTitle",[dijit._Widget, dijit._TemplatedMixin, dijit._Contained],{
  24. // summary:
  25. // SImple internal, non-clickable, menu entry to act as a menu title bar.
  26. templateString: "<tr><td dojoAttachPoint=\"title\" colspan=\"4\" class=\"dijitToolbar\" style=\"font-weight: bold; padding: 3px;\"></td></tr>",
  27. menuTitle: "",
  28. postCreate: function(){
  29. dojo.setSelectable(this.domNode, false);
  30. var label = this.id+"_text";
  31. this.domNode.setAttribute("aria-labelledby", label);
  32. },
  33. _setMenuTitleAttr: function(str){
  34. this.title.innerHTML = str;
  35. },
  36. _getMenuTitleAttr: function(str){
  37. return this.title.innerHTML;
  38. }
  39. });
  40. dojo.declare("dojox.editor.plugins.Breadcrumb",dijit._editor._Plugin,{
  41. // summary:
  42. // This plugin provides Breadcrumb cabability to the editor. When
  43. // As you move around the editor, it updates with your current indention
  44. // depth.
  45. // _menu: [private]
  46. // The popup menu that is displayed.
  47. _menu: null,
  48. // breadcrumbBar: [protected]
  49. // The toolbar containing the breadcrumb.
  50. breadcrumbBar: null,
  51. setEditor: function(editor){
  52. // summary:
  53. // Over-ride for the setting of the editor.
  54. // editor: Object
  55. // The editor to configure for this plugin to use.
  56. this.editor = editor;
  57. this._buttons = [];
  58. this.breadcrumbBar = new dijit.Toolbar();
  59. var strings = dojo.i18n.getLocalization("dojox.editor.plugins", "Breadcrumb");
  60. this._titleTemplate = strings.nodeActions;
  61. dojo.place(this.breadcrumbBar.domNode, editor.footer);
  62. this.editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
  63. this._menu = new dijit.Menu({});
  64. dojo.addClass(this.breadcrumbBar.domNode, "dojoxEditorBreadcrumbArrow");
  65. var self = this;
  66. var body = new dijit.form.ComboButton({
  67. showLabel: true,
  68. label: "body",
  69. _selNode: editor.editNode,
  70. dropDown: this._menu,
  71. onClick: dojo.hitch(this, function(){
  72. this._menuTarget = editor.editNode;
  73. this._selectContents();
  74. })
  75. });
  76. // Build the menu
  77. this._menuTitle = new dojox.editor.plugins._BreadcrumbMenuTitle({menuTitle: strings.nodeActions});
  78. this._selCMenu = new dijit.MenuItem({label: strings.selectContents, onClick: dojo.hitch(this, this._selectContents)});
  79. this._delCMenu = new dijit.MenuItem({label: strings.deleteContents, onClick: dojo.hitch(this, this._deleteContents)});
  80. this._selEMenu = new dijit.MenuItem({label: strings.selectElement, onClick: dojo.hitch(this, this._selectElement)});
  81. this._delEMenu = new dijit.MenuItem({label: strings.deleteElement, onClick: dojo.hitch(this, this._deleteElement)});
  82. this._moveSMenu = new dijit.MenuItem({label: strings.moveStart, onClick: dojo.hitch(this, this._moveCToStart)});
  83. this._moveEMenu = new dijit.MenuItem({label: strings.moveEnd, onClick: dojo.hitch(this, this._moveCToEnd)});
  84. this._menu.addChild(this._menuTitle);
  85. this._menu.addChild(this._selCMenu);
  86. this._menu.addChild(this._delCMenu);
  87. this._menu.addChild(new dijit.MenuSeparator({}));
  88. this._menu.addChild(this._selEMenu);
  89. this._menu.addChild(this._delEMenu);
  90. this._menu.addChild(new dijit.MenuSeparator({}));
  91. this._menu.addChild(this._moveSMenu);
  92. this._menu.addChild(this._moveEMenu);
  93. body._ddConnect = dojo.connect(body, "openDropDown", dojo.hitch(this, function(){
  94. this._menuTarget = body._selNode;
  95. this._menuTitle.set("menuTitle", dojo.string.substitute(this._titleTemplate,{
  96. "nodeName": "&lt;body&gt;"
  97. }));
  98. this._selEMenu.set("disabled", true);
  99. this._delEMenu.set("disabled", true);
  100. this._selCMenu.set("disabled", false);
  101. this._delCMenu.set("disabled", false);
  102. this._moveSMenu.set("disabled", false);
  103. this._moveEMenu.set("disabled", false);
  104. }));
  105. this.breadcrumbBar.addChild(body);
  106. this.connect(this.editor, "onNormalizedDisplayChanged", "updateState");
  107. }));
  108. this.breadcrumbBar.startup();
  109. if(dojo.isIE){
  110. // Sometimes IE will mess up layout and needs to be poked.
  111. setTimeout(dojo.hitch(this, function(){this.breadcrumbBar.domNode.className = this.breadcrumbBar.domNode.className;}), 100);
  112. }
  113. },
  114. _selectContents: function(){
  115. // summary:
  116. // Internal function for selecting the contents of a node.
  117. this.editor.focus();
  118. if(this._menuTarget){
  119. var nodeName = this._menuTarget.tagName.toLowerCase();
  120. switch(nodeName){
  121. case 'br':
  122. case 'hr':
  123. case 'img':
  124. case 'input':
  125. case 'base':
  126. case 'meta':
  127. case 'area':
  128. case 'basefont':
  129. break;
  130. default:
  131. try{
  132. dojo.withGlobal(this.editor.window,
  133. "collapse", dijit._editor.selection, [null]);
  134. dojo.withGlobal(this.editor.window,
  135. "selectElementChildren", dijit._editor.selection, [this._menuTarget]);
  136. this.editor.onDisplayChanged();
  137. }catch(e){/*squelch*/}
  138. }
  139. }
  140. },
  141. _deleteContents: function(){
  142. // summary:
  143. // Internal function for selecting the contents of a node.
  144. if(this._menuTarget){
  145. this.editor.beginEditing();
  146. this._selectContents();
  147. dojo.withGlobal(this.editor.window,
  148. "remove", dijit._editor.selection, [this._menuTarget]);
  149. this.editor.endEditing();
  150. this._updateBreadcrumb();
  151. this.editor.onDisplayChanged();
  152. }
  153. },
  154. _selectElement: function(){
  155. // summary:
  156. // Internal function for selecting the contents of a node.
  157. this.editor.focus();
  158. if(this._menuTarget){
  159. dojo.withGlobal(this.editor.window,
  160. "collapse", dijit._editor.selection, [null]);
  161. dojo.withGlobal(this.editor.window,
  162. "selectElement", dijit._editor.selection, [this._menuTarget]);
  163. this.editor.onDisplayChanged();
  164. }
  165. },
  166. _deleteElement: function(){
  167. // summary:
  168. // Internal function for selecting the contents of a node.
  169. if(this._menuTarget){
  170. this.editor.beginEditing();
  171. this._selectElement();
  172. dojo.withGlobal(this.editor.window,
  173. "remove", dijit._editor.selection, [this._menuTarget]);
  174. this.editor.endEditing();
  175. this._updateBreadcrumb();
  176. this.editor.onDisplayChanged();
  177. }
  178. },
  179. _moveCToStart: function(){
  180. // summary:
  181. // Internal function for selecting the contents of a node.
  182. this.editor.focus();
  183. if(this._menuTarget){
  184. this._selectContents();
  185. dojo.withGlobal(this.editor.window,
  186. "collapse", dijit._editor.selection, [true]);
  187. }
  188. },
  189. _moveCToEnd: function(){
  190. // summary:
  191. // Internal function for selecting the contents of a node.
  192. this.editor.focus();
  193. if(this._menuTarget){
  194. this._selectContents();
  195. dojo.withGlobal(this.editor.window,
  196. "collapse", dijit._editor.selection, [false]);
  197. }
  198. },
  199. _updateBreadcrumb: function(){
  200. // summary:
  201. // Function to trigger updating of the breadcrumb
  202. // tags:
  203. // private
  204. var ed = this.editor;
  205. if(ed.window){
  206. var sel = dijit.range.getSelection(ed.window);
  207. if(sel && sel.rangeCount > 0){
  208. var range = sel.getRangeAt(0);
  209. // Check the getSelectedElement call. Needed when dealing with img tags.
  210. var node = dojo.withGlobal(ed.window,
  211. "getSelectedElement", dijit._editor.selection) || range.startContainer;
  212. //var node = range.startContainer;
  213. var bcList = [];
  214. // Make sure we get a selection within the editor document,
  215. // have seen cases on IE where this wasn't true.
  216. if(node && node.ownerDocument === ed.document){
  217. while(node && node !== ed.editNode && node != ed.document.body && node != ed.document){
  218. if(node.nodeType === 1){
  219. bcList.push({type: node.tagName.toLowerCase(), node: node});
  220. }
  221. node = node.parentNode;
  222. }
  223. bcList = bcList.reverse();
  224. while(this._buttons.length){
  225. var db = this._buttons.pop();
  226. dojo.disconnect(db._ddConnect);
  227. this.breadcrumbBar.removeChild(db);
  228. }
  229. this._buttons = [];
  230. var i;
  231. var self = this;
  232. for(i = 0; i < bcList.length; i++){
  233. var bc = bcList[i];
  234. var b = new dijit.form.ComboButton({
  235. showLabel: true,
  236. label: bc.type,
  237. _selNode: bc.node,
  238. dropDown: this._menu,
  239. onClick: function(){
  240. self._menuTarget = this._selNode;
  241. self._selectContents();
  242. }
  243. });
  244. b._ddConnect = dojo.connect(b, "openDropDown", dojo.hitch(b, function(){
  245. self._menuTarget = this._selNode;
  246. var nodeName = self._menuTarget.tagName.toLowerCase();
  247. var title = dojo.string.substitute(self._titleTemplate,{
  248. "nodeName": "&lt;" + nodeName + "&gt;"
  249. });
  250. self._menuTitle.set("menuTitle", title);
  251. switch(nodeName){
  252. case 'br':
  253. case 'hr':
  254. case 'img':
  255. case 'input':
  256. case 'base':
  257. case 'meta':
  258. case 'area':
  259. case 'basefont':
  260. self._selCMenu.set("disabled", true);
  261. self._delCMenu.set("disabled", true);
  262. self._moveSMenu.set("disabled", true);
  263. self._moveEMenu.set("disabled", true);
  264. self._selEMenu.set("disabled", false);
  265. self._delEMenu.set("disabled", false);
  266. break;
  267. default:
  268. self._selCMenu.set("disabled", false);
  269. self._delCMenu.set("disabled", false);
  270. self._selEMenu.set("disabled", false);
  271. self._delEMenu.set("disabled", false);
  272. self._moveSMenu.set("disabled", false);
  273. self._moveEMenu.set("disabled", false);
  274. }
  275. }));
  276. this._buttons.push(b);
  277. this.breadcrumbBar.addChild(b);
  278. }
  279. if(dojo.isIE){
  280. // Prod it to fix layout.
  281. this.breadcrumbBar.domNode.className = this.breadcrumbBar.domNode.className;
  282. }
  283. }
  284. }
  285. }
  286. },
  287. updateState: function(){
  288. // summary:
  289. // Over-ride of updateState to hide the toolbar when the iframe is not visible.
  290. // Also triggers the breadcrumb update.
  291. if(dojo.style(this.editor.iframe, "display") === "none" || this.get("disabled")){
  292. dojo.style(this.breadcrumbBar.domNode, "display", "none");
  293. }else{
  294. if(dojo.style(this.breadcrumbBar.domNode, "display") === "none"){
  295. dojo.style(this.breadcrumbBar.domNode, "display", "block");
  296. }
  297. this._updateBreadcrumb();
  298. // Some themes do padding, so we have to resize again after display.
  299. var size = dojo.marginBox(this.editor.domNode);
  300. this.editor.resize({h: size.h});
  301. }
  302. },
  303. destroy: function(){
  304. // summary:
  305. // Over-ride to clean up the breadcrumb toolbar.
  306. if(this.breadcrumbBar){
  307. this.breadcrumbBar.destroyRecursive();
  308. this.breadcrumbBar = null;
  309. }
  310. if(this._menu){
  311. this._menu.destroyRecursive();
  312. delete this._menu;
  313. }
  314. this._buttons = null;
  315. delete this.editor.breadcrumbBar;
  316. this.inherited(arguments);
  317. }
  318. });
  319. // Register this plugin.
  320. dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
  321. if(o.plugin){ return; }
  322. var name = o.args.name.toLowerCase();
  323. if(name === "breadcrumb"){
  324. o.plugin = new dojox.editor.plugins.Breadcrumb({});
  325. }
  326. });
  327. return dojox.editor.plugins.Breadcrumb;
  328. });