dojox/editor/plugins/TablePlugins.js

  • Provides:

    • dojox.editor.plugins.TablePlugins
  • dojox.editor.plugins._TableHandler

    • type
      Function
    • chains:
      • dijit._editor._Plugin: (prototype)
      • dijit._editor._Plugin: (call)
    • summary
      A global object that handles common tasks for all the plugins. Since
      there are several plugins that are all calling common methods, it's preferable
      that they call a centralized location that either has a set variable or a
      timeout to only repeat code-heavy calls when necessary.
  • dojox.editor.plugins._TableHandler.tablesConnected

    • summary
  • dojox.editor.plugins._TableHandler.currentlyAvailable

    • summary
  • dojox.editor.plugins._TableHandler.alwaysAvailable

    • summary
  • dojox.editor.plugins._TableHandler.availableCurrentlySet

    • summary
  • dojox.editor.plugins._TableHandler.initialized

    • summary
  • dojox.editor.plugins._TableHandler.tableData

    • summary
  • dojox.editor.plugins._TableHandler.shiftKeyDown

    • summary
  • dojox.editor.plugins._TableHandler.editorDomNode

    • summary
  • dojox.editor.plugins._TableHandler.undoEnabled

    • summary
  • dojox.editor.plugins._TableHandler.refCount

    • summary
  • dojox.editor.plugins._TableHandler.doMixins

    • type
      Function
    • source: [view]
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });
    • summary
  • dojox.editor.plugins._TableHandler.initialize

    • type
      Function
    • parameters:
      • editor: (typeof )
    • source: [view]
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
    • summary
      Initialize the global handler upon a plugin's first instance of setEditor
      
      
      All plugins will attempt initialization. We only need to do so once.
      But keep track so that it is cleaned up when all usage of it for an editor has
      been removed.
  • dojox.editor.plugins._TableHandler.getTableInfo

    • type
      Function
    • parameters:
      • forceNewData: (typeof )
    • source: [view]
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
    • summary
      Gets the table in focus
      Collects info on the table - see return params
  • dojox.editor.plugins._TableHandler.connectDraggable

    • type
      Function
    • source: [view]
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
    • summary
      Detects drag-n-drop in the editor (could probably be moved to there)
      Currently only checks if item dragged was a TABLE, and removes its align attr
      DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
  • dojox.editor.plugins._TableHandler.onDragStart

    • type
      Function
    • source: [view]
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
    • summary
  • dojox.editor.plugins._TableHandler.onDragEnd

    • type
      Function
    • source: [view]
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
    • summary
      Detects that an object has been dragged into place
      Currently, this code is only used for when a table is dragged
      and clears the "align" attribute, so that the table will look
      to be more in the place that the user expected.
      TODO: This code can be used for other things, most
      notably UNDO, which currently is not quite usable.
      This code could also find itself in the Editor code when it is
      complete.
      
      console.log("onDragEnd");
  • dojox.editor.plugins._TableHandler.checkAvailable

    • type
      Function
    • source: [view]
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
    • summary
      For table plugs
      Checking if a table or part of a table has focus so that
      Plugs can change their status
  • dojox.editor.plugins._TableHandler._prepareTable

    • type
      Function
    • parameters:
      • tbl: (typeof )
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });


       },
       initialize: function(editor){
        // summary:
        //  Initialize the global handler upon a plugin's first instance of setEditor
        //

        
        // All plugins will attempt initialization. We only need to do so once.
        // But keep track so that it is cleaned up when all usage of it for an editor has
        // been removed.
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
       },

       
       getTableInfo: function(forceNewData){
        // summary:
        //  Gets the table in focus
        //  Collects info on the table - see return params
        //
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
       },

       
       connectDraggable: function(){
        // summary:
        //  Detects drag-n-drop in the editor (could probably be moved to there)
        //  Currently only checks if item dragged was a TABLE, and removes its align attr
        //  DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
        //
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
       },
       onDragStart: function(){
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
       },
       onDragEnd: function(){
        // summary:
        //  Detects that an object has been dragged into place
        //  Currently, this code is only used for when a table is dragged
        //  and clears the "align" attribute, so that the table will look
        //  to be more in the place that the user expected.
        //  TODO: This code can be used for other things, most
        //  notably UNDO, which currently is not quite usable.
        //  This code could also find itself in the Editor code when it is
        //  complete.

        
        //console.log("onDragEnd");
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
       },
       checkAvailable: function(){
        // summary:
        //  For table plugs
        //  Checking if a table or part of a table has focus so that
        //  Plugs can change their status
        //
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
       },

       
       _prepareTable: function(tbl){
        // For IE's sake, we are adding IDs to the TDs if none is there
        // We go ahead and use it for other code for convenience
        //
        var tds = this.editor.query("td", tbl);
        console.log("prep:", tds, tbl);
        if(!tds[0].id){
         tds.forEach(function(td, i){
          if(!td.id){
           td.id = "tdid"+i+this.getTimeStamp();
          }
         }, this);
        }
        return tds;
    • summary
  • dojox.editor.plugins._TableHandler.getTimeStamp

    • type
      Function
    • source: [view]
        return new Date().getTime(); // Fixed the bug that this method always returns the same timestamp
      //  return Math.floor(new Date().getTime() * 0.00000001);
    • returns
      Fixed the bug that this method always returns the same timestamp
    • summary
  • dojox.editor.plugins._TableHandler._tempStoreTableData

    • type
      Function
    • parameters:
      • type: (typeof )
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });


       },
       initialize: function(editor){
        // summary:
        //  Initialize the global handler upon a plugin's first instance of setEditor
        //

        
        // All plugins will attempt initialization. We only need to do so once.
        // But keep track so that it is cleaned up when all usage of it for an editor has
        // been removed.
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
       },

       
       getTableInfo: function(forceNewData){
        // summary:
        //  Gets the table in focus
        //  Collects info on the table - see return params
        //
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
       },

       
       connectDraggable: function(){
        // summary:
        //  Detects drag-n-drop in the editor (could probably be moved to there)
        //  Currently only checks if item dragged was a TABLE, and removes its align attr
        //  DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
        //
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
       },
       onDragStart: function(){
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
       },
       onDragEnd: function(){
        // summary:
        //  Detects that an object has been dragged into place
        //  Currently, this code is only used for when a table is dragged
        //  and clears the "align" attribute, so that the table will look
        //  to be more in the place that the user expected.
        //  TODO: This code can be used for other things, most
        //  notably UNDO, which currently is not quite usable.
        //  This code could also find itself in the Editor code when it is
        //  complete.

        
        //console.log("onDragEnd");
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
       },
       checkAvailable: function(){
        // summary:
        //  For table plugs
        //  Checking if a table or part of a table has focus so that
        //  Plugs can change their status
        //
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
       },

       
       _prepareTable: function(tbl){
        // For IE's sake, we are adding IDs to the TDs if none is there
        // We go ahead and use it for other code for convenience
        //
        var tds = this.editor.query("td", tbl);
        console.log("prep:", tds, tbl);
        if(!tds[0].id){
         tds.forEach(function(td, i){
          if(!td.id){
           td.id = "tdid"+i+this.getTimeStamp();
          }
         }, this);
        }
        return tds;
       },

       
       getTimeStamp: function(){
        return new Date().getTime(); // Fixed the bug that this method always returns the same timestamp
      //  return Math.floor(new Date().getTime() * 0.00000001);
       },

       
       _tempStoreTableData: function(type){
        // caching or clearing table data, depending on the arg
        //
        if(type===true){
         //store indefinitely
        }else if(type===false){
         // clear object
         this.tableData = null;
        }else if(type===undefined){
         console.warn("_tempStoreTableData must be passed an argument");
        }else{
         // type is a number/ms
         setTimeout(dojo.hitch(this, function(){
          this.tableData = null;
         }), type);
        }
    • returns
      Fixed the bug that this method always returns the same timestamp
    • summary
  • dojox.editor.plugins._TableHandler._tempAvailability

    • type
      Function
    • parameters:
      • type: (typeof )
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });


       },
       initialize: function(editor){
        // summary:
        //  Initialize the global handler upon a plugin's first instance of setEditor
        //

        
        // All plugins will attempt initialization. We only need to do so once.
        // But keep track so that it is cleaned up when all usage of it for an editor has
        // been removed.
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
       },

       
       getTableInfo: function(forceNewData){
        // summary:
        //  Gets the table in focus
        //  Collects info on the table - see return params
        //
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
       },

       
       connectDraggable: function(){
        // summary:
        //  Detects drag-n-drop in the editor (could probably be moved to there)
        //  Currently only checks if item dragged was a TABLE, and removes its align attr
        //  DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
        //
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
       },
       onDragStart: function(){
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
       },
       onDragEnd: function(){
        // summary:
        //  Detects that an object has been dragged into place
        //  Currently, this code is only used for when a table is dragged
        //  and clears the "align" attribute, so that the table will look
        //  to be more in the place that the user expected.
        //  TODO: This code can be used for other things, most
        //  notably UNDO, which currently is not quite usable.
        //  This code could also find itself in the Editor code when it is
        //  complete.

        
        //console.log("onDragEnd");
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
       },
       checkAvailable: function(){
        // summary:
        //  For table plugs
        //  Checking if a table or part of a table has focus so that
        //  Plugs can change their status
        //
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
       },

       
       _prepareTable: function(tbl){
        // For IE's sake, we are adding IDs to the TDs if none is there
        // We go ahead and use it for other code for convenience
        //
        var tds = this.editor.query("td", tbl);
        console.log("prep:", tds, tbl);
        if(!tds[0].id){
         tds.forEach(function(td, i){
          if(!td.id){
           td.id = "tdid"+i+this.getTimeStamp();
          }
         }, this);
        }
        return tds;
       },

       
       getTimeStamp: function(){
        return new Date().getTime(); // Fixed the bug that this method always returns the same timestamp
      //  return Math.floor(new Date().getTime() * 0.00000001);
       },

       
       _tempStoreTableData: function(type){
        // caching or clearing table data, depending on the arg
        //
        if(type===true){
         //store indefinitely
        }else if(type===false){
         // clear object
         this.tableData = null;
        }else if(type===undefined){
         console.warn("_tempStoreTableData must be passed an argument");
        }else{
         // type is a number/ms
         setTimeout(dojo.hitch(this, function(){
          this.tableData = null;
         }), type);
        }
       },

       
       _tempAvailability: function(type){
         // caching or clearing availability, depending on the arg
        if(type===true){
         //store indefinitely
         this.availableCurrentlySet = true;
        }else if(type===false){
         // clear object
         this.availableCurrentlySet = false;
        }else if(type===undefined){
         console.warn("_tempAvailability must be passed an argument");
        }else{
         // type is a number/ms
         this.availableCurrentlySet = true;
         setTimeout(dojo.hitch(this, function(){
          this.availableCurrentlySet = false;
         }), type);
        }
    • returns
      Fixed the bug that this method always returns the same timestamp
    • summary
  • dojox.editor.plugins._TableHandler.connectTableKeys

    • type
      Function
    • source: [view]
        if(this.tablesConnected){ return; }
        this.tablesConnected = true;
        var node = (this.editor.iframe) ? this.editor.document : this.editor.editNode;
        this.cnKeyDn = dojo.connect(node, "onkeydown", this, "onKeyDown");
        this.cnKeyUp = dojo.connect(node, "onkeyup", this, "onKeyUp");
        this._myListeners.push(dojo.connect(node, "onkeypress", this, "onKeyUp"));
    • summary
      When a table is in focus, start detecting keys
      Mainly checking for the TAB key so user can tab
      through a table (blocking the browser's desire to
      tab away from teh editor completely)
  • dojox.editor.plugins._TableHandler.disconnectTableKeys

    • type
      Function
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });


       },
       initialize: function(editor){
        // summary:
        //  Initialize the global handler upon a plugin's first instance of setEditor
        //

        
        // All plugins will attempt initialization. We only need to do so once.
        // But keep track so that it is cleaned up when all usage of it for an editor has
        // been removed.
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
       },

       
       getTableInfo: function(forceNewData){
        // summary:
        //  Gets the table in focus
        //  Collects info on the table - see return params
        //
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
       },

       
       connectDraggable: function(){
        // summary:
        //  Detects drag-n-drop in the editor (could probably be moved to there)
        //  Currently only checks if item dragged was a TABLE, and removes its align attr
        //  DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
        //
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
       },
       onDragStart: function(){
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
       },
       onDragEnd: function(){
        // summary:
        //  Detects that an object has been dragged into place
        //  Currently, this code is only used for when a table is dragged
        //  and clears the "align" attribute, so that the table will look
        //  to be more in the place that the user expected.
        //  TODO: This code can be used for other things, most
        //  notably UNDO, which currently is not quite usable.
        //  This code could also find itself in the Editor code when it is
        //  complete.

        
        //console.log("onDragEnd");
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
       },
       checkAvailable: function(){
        // summary:
        //  For table plugs
        //  Checking if a table or part of a table has focus so that
        //  Plugs can change their status
        //
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
       },

       
       _prepareTable: function(tbl){
        // For IE's sake, we are adding IDs to the TDs if none is there
        // We go ahead and use it for other code for convenience
        //
        var tds = this.editor.query("td", tbl);
        console.log("prep:", tds, tbl);
        if(!tds[0].id){
         tds.forEach(function(td, i){
          if(!td.id){
           td.id = "tdid"+i+this.getTimeStamp();
          }
         }, this);
        }
        return tds;
       },

       
       getTimeStamp: function(){
        return new Date().getTime(); // Fixed the bug that this method always returns the same timestamp
      //  return Math.floor(new Date().getTime() * 0.00000001);
       },

       
       _tempStoreTableData: function(type){
        // caching or clearing table data, depending on the arg
        //
        if(type===true){
         //store indefinitely
        }else if(type===false){
         // clear object
         this.tableData = null;
        }else if(type===undefined){
         console.warn("_tempStoreTableData must be passed an argument");
        }else{
         // type is a number/ms
         setTimeout(dojo.hitch(this, function(){
          this.tableData = null;
         }), type);
        }
       },

       
       _tempAvailability: function(type){
         // caching or clearing availability, depending on the arg
        if(type===true){
         //store indefinitely
         this.availableCurrentlySet = true;
        }else if(type===false){
         // clear object
         this.availableCurrentlySet = false;
        }else if(type===undefined){
         console.warn("_tempAvailability must be passed an argument");
        }else{
         // type is a number/ms
         this.availableCurrentlySet = true;
         setTimeout(dojo.hitch(this, function(){
          this.availableCurrentlySet = false;
         }), type);
        }

        
       },

       
       connectTableKeys: function(){
        // summary:
        //  When a table is in focus, start detecting keys
        //  Mainly checking for the TAB key so user can tab
        //  through a table (blocking the browser's desire to
        //  tab away from teh editor completely)
        if(this.tablesConnected){ return; }
        this.tablesConnected = true;
        var node = (this.editor.iframe) ? this.editor.document : this.editor.editNode;
        this.cnKeyDn = dojo.connect(node, "onkeydown", this, "onKeyDown");
        this.cnKeyUp = dojo.connect(node, "onkeyup", this, "onKeyUp");
        this._myListeners.push(dojo.connect(node, "onkeypress", this, "onKeyUp"));
       },

       
       disconnectTableKeys: function(){
        //console.log("disconnect")
        dojo.disconnect(this.cnKeyDn);
        dojo.disconnect(this.cnKeyUp);
        this.tablesConnected = false;
    • returns
      Fixed the bug that this method always returns the same timestamp
    • summary
  • dojox.editor.plugins._TableHandler.onKeyDown

    • type
      Function
    • parameters:
      • evt: (typeof )
    • source: [view]
        var key = evt.keyCode;
        //console.log(" -> DOWN:", key);
        if(key == 16){ this.shiftKeyDown = true;}
        if(key == 9) {
         var o = this.getTableInfo();
         //console.log("TAB ", o.tdIndex, o);
         // modifying the o.tdIndex in the tableData directly, because we may save it
         // FIXME: tabTo is a global
         o.tdIndex = (this.shiftKeyDown) ? o.tdIndex-1 : tabTo = o.tdIndex+1;
         if(o.tdIndex>=0 && o.tdIndex
          
          this.editor.selectElement(o.tds[o.tdIndex]);

          
          // we know we are still within a table, so block the need
          // to run the method
          this.currentlyAvailable = true;
          this._tempAvailability(true);
          //
          this._tempStoreTableData(true);
          this.stopEvent = true;
         }else{
          //tabbed out of table
          this.stopEvent = false;
          this.onDisplayChanged();
         }
         if(this.stopEvent) {
          dojo.stopEvent(evt);
         }
        }
    • summary
  • dojox.editor.plugins._TableHandler.onKeyUp

    • type
      Function
    • parameters:
      • evt: (typeof )
    • source: [view]
        var key = evt.keyCode;
        //console.log(" -> UP:", key)
        if(key == 16){ this.shiftKeyDown = false;}
        if(key == 37 || key == 38 || key == 39 || key == 40 ){
         // user can arrow or tab out of table - need to recheck
         this.onDisplayChanged();
        }
        if(key == 9 && this.stopEvent){ dojo.stopEvent(evt);}
    • summary
  • dojox.editor.plugins._TableHandler.onDisplayChanged

    • type
      Function
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });


       },
       initialize: function(editor){
        // summary:
        //  Initialize the global handler upon a plugin's first instance of setEditor
        //

        
        // All plugins will attempt initialization. We only need to do so once.
        // But keep track so that it is cleaned up when all usage of it for an editor has
        // been removed.
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
       },

       
       getTableInfo: function(forceNewData){
        // summary:
        //  Gets the table in focus
        //  Collects info on the table - see return params
        //
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
       },

       
       connectDraggable: function(){
        // summary:
        //  Detects drag-n-drop in the editor (could probably be moved to there)
        //  Currently only checks if item dragged was a TABLE, and removes its align attr
        //  DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
        //
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
       },
       onDragStart: function(){
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
       },
       onDragEnd: function(){
        // summary:
        //  Detects that an object has been dragged into place
        //  Currently, this code is only used for when a table is dragged
        //  and clears the "align" attribute, so that the table will look
        //  to be more in the place that the user expected.
        //  TODO: This code can be used for other things, most
        //  notably UNDO, which currently is not quite usable.
        //  This code could also find itself in the Editor code when it is
        //  complete.

        
        //console.log("onDragEnd");
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
       },
       checkAvailable: function(){
        // summary:
        //  For table plugs
        //  Checking if a table or part of a table has focus so that
        //  Plugs can change their status
        //
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
       },

       
       _prepareTable: function(tbl){
        // For IE's sake, we are adding IDs to the TDs if none is there
        // We go ahead and use it for other code for convenience
        //
        var tds = this.editor.query("td", tbl);
        console.log("prep:", tds, tbl);
        if(!tds[0].id){
         tds.forEach(function(td, i){
          if(!td.id){
           td.id = "tdid"+i+this.getTimeStamp();
          }
         }, this);
        }
        return tds;
       },

       
       getTimeStamp: function(){
        return new Date().getTime(); // Fixed the bug that this method always returns the same timestamp
      //  return Math.floor(new Date().getTime() * 0.00000001);
       },

       
       _tempStoreTableData: function(type){
        // caching or clearing table data, depending on the arg
        //
        if(type===true){
         //store indefinitely
        }else if(type===false){
         // clear object
         this.tableData = null;
        }else if(type===undefined){
         console.warn("_tempStoreTableData must be passed an argument");
        }else{
         // type is a number/ms
         setTimeout(dojo.hitch(this, function(){
          this.tableData = null;
         }), type);
        }
       },

       
       _tempAvailability: function(type){
         // caching or clearing availability, depending on the arg
        if(type===true){
         //store indefinitely
         this.availableCurrentlySet = true;
        }else if(type===false){
         // clear object
         this.availableCurrentlySet = false;
        }else if(type===undefined){
         console.warn("_tempAvailability must be passed an argument");
        }else{
         // type is a number/ms
         this.availableCurrentlySet = true;
         setTimeout(dojo.hitch(this, function(){
          this.availableCurrentlySet = false;
         }), type);
        }

        
       },

       
       connectTableKeys: function(){
        // summary:
        //  When a table is in focus, start detecting keys
        //  Mainly checking for the TAB key so user can tab
        //  through a table (blocking the browser's desire to
        //  tab away from teh editor completely)
        if(this.tablesConnected){ return; }
        this.tablesConnected = true;
        var node = (this.editor.iframe) ? this.editor.document : this.editor.editNode;
        this.cnKeyDn = dojo.connect(node, "onkeydown", this, "onKeyDown");
        this.cnKeyUp = dojo.connect(node, "onkeyup", this, "onKeyUp");
        this._myListeners.push(dojo.connect(node, "onkeypress", this, "onKeyUp"));
       },

       
       disconnectTableKeys: function(){
        //console.log("disconnect")
        dojo.disconnect(this.cnKeyDn);
        dojo.disconnect(this.cnKeyUp);
        this.tablesConnected = false;
       },

       
       onKeyDown: function(evt){
        var key = evt.keyCode;
        //console.log(" -> DOWN:", key);
        if(key == 16){ this.shiftKeyDown = true;}
        if(key == 9) {
         var o = this.getTableInfo();
         //console.log("TAB ", o.tdIndex, o);
         // modifying the o.tdIndex in the tableData directly, because we may save it
         // FIXME: tabTo is a global
         o.tdIndex = (this.shiftKeyDown) ? o.tdIndex-1 : tabTo = o.tdIndex+1;
         if(o.tdIndex>=0 && o.tdIndex
          
          this.editor.selectElement(o.tds[o.tdIndex]);

          
          // we know we are still within a table, so block the need
          // to run the method
          this.currentlyAvailable = true;
          this._tempAvailability(true);
          //
          this._tempStoreTableData(true);
          this.stopEvent = true;
         }else{
          //tabbed out of table
          this.stopEvent = false;
          this.onDisplayChanged();
         }
         if(this.stopEvent) {
          dojo.stopEvent(evt);
         }
        }
       },

       
       onKeyUp: function(evt){
        var key = evt.keyCode;
        //console.log(" -> UP:", key)
        if(key == 16){ this.shiftKeyDown = false;}
        if(key == 37 || key == 38 || key == 39 || key == 40 ){
         // user can arrow or tab out of table - need to recheck
         this.onDisplayChanged();
        }
        if(key == 9 && this.stopEvent){ dojo.stopEvent(evt);}
       },

       
       onDisplayChanged: function(){
        //console.log("onDisplayChanged")
        this.currentlyAvailable = false;
        this._tempStoreTableData(false);
        this._tempAvailability(false);
        this.checkAvailable();
    • returns
      Fixed the bug that this method always returns the same timestamp
    • summary
  • dojox.editor.plugins._TableHandler.uninitialize

    • type
      Function
    • parameters:
      • editor: (typeof The)
        editor to detach from.
    • source: [view]
        if(this.editor == editor){
         this.refCount--;
         if(!this.refCount && this.initialized){
          if(this.tablesConnected){
           this.disconnectTableKeys();
          }
          this.initialized = false;
          dojo.forEach(this._myListeners, function(l){
           dojo.disconnect(l);
          });
          delete this._myListeners;
          delete this.editor._tablePluginHandler;
          delete this.editor;
         }
         this.inherited(arguments);
        }
    • summary
      Function to handle cleaning up of connects
      and such.  It only finally destroys everything once
      all 'references' to it have gone.  As in all plugins
      that called init on it destroyed their refs in their
      cleanup calls.
  • dojox.editor.plugins._TableHandler.editor

    • summary
  • dojox.editor.plugins._TableHandler.editor._tablePluginHandler

    • summary
  • dojox.editor.plugins._TableHandler._myListeners

    • summary
  • dojox.editor.plugins._TableHandler.editorDomNode.ondragstart

    • summary
  • dojox.editor.plugins._TableHandler.editorDomNode.ondragend

    • summary
  • dojox.editor.plugins._TableHandler.cnKeyDn

    • summary
  • dojox.editor.plugins._TableHandler.cnKeyUp

    • summary
  • dojox.editor.plugins._TableHandler.stopEvent

    • summary
  • dojox.editor.plugins._TableHandler.uninitialize.editor

    • type
      The
    • summary
      editor to detach from.
  • dojox.editor.plugins.TablePlugins

    • type
      Function
    • chains:
      • dijit._editor._Plugin: (prototype)
      • dijit._editor._Plugin: (call)
    • summary
      A collection of Plugins for inserting and modifying tables in the Editor
      See end of this document for all avaiable plugs
      and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      
      NOT IMPLEMENTED: Not handling cell merge, span or split
  • dojox.editor.plugins.TablePlugins.iconClassPrefix

    • summary
  • dojox.editor.plugins.TablePlugins.useDefaultCommand

    • summary
  • dojox.editor.plugins.TablePlugins.buttonClass

    • summary
  • dojox.editor.plugins.TablePlugins.commandName

    • summary
  • dojox.editor.plugins.TablePlugins.label

    • summary
  • dojox.editor.plugins.TablePlugins.alwaysAvailable

    • summary
  • dojox.editor.plugins.TablePlugins.undoEnabled

    • summary
  • dojox.editor.plugins.TablePlugins.onDisplayChanged

    • type
      Function
    • parameters:
      • withinTable: (typeof )
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });


       },
       initialize: function(editor){
        // summary:
        //  Initialize the global handler upon a plugin's first instance of setEditor
        //

        
        // All plugins will attempt initialization. We only need to do so once.
        // But keep track so that it is cleaned up when all usage of it for an editor has
        // been removed.
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
       },

       
       getTableInfo: function(forceNewData){
        // summary:
        //  Gets the table in focus
        //  Collects info on the table - see return params
        //
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
       },

       
       connectDraggable: function(){
        // summary:
        //  Detects drag-n-drop in the editor (could probably be moved to there)
        //  Currently only checks if item dragged was a TABLE, and removes its align attr
        //  DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
        //
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
       },
       onDragStart: function(){
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
       },
       onDragEnd: function(){
        // summary:
        //  Detects that an object has been dragged into place
        //  Currently, this code is only used for when a table is dragged
        //  and clears the "align" attribute, so that the table will look
        //  to be more in the place that the user expected.
        //  TODO: This code can be used for other things, most
        //  notably UNDO, which currently is not quite usable.
        //  This code could also find itself in the Editor code when it is
        //  complete.

        
        //console.log("onDragEnd");
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
       },
       checkAvailable: function(){
        // summary:
        //  For table plugs
        //  Checking if a table or part of a table has focus so that
        //  Plugs can change their status
        //
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
       },

       
       _prepareTable: function(tbl){
        // For IE's sake, we are adding IDs to the TDs if none is there
        // We go ahead and use it for other code for convenience
        //
        var tds = this.editor.query("td", tbl);
        console.log("prep:", tds, tbl);
        if(!tds[0].id){
         tds.forEach(function(td, i){
          if(!td.id){
           td.id = "tdid"+i+this.getTimeStamp();
          }
         }, this);
        }
        return tds;
       },

       
       getTimeStamp: function(){
        return new Date().getTime(); // Fixed the bug that this method always returns the same timestamp
      //  return Math.floor(new Date().getTime() * 0.00000001);
       },

       
       _tempStoreTableData: function(type){
        // caching or clearing table data, depending on the arg
        //
        if(type===true){
         //store indefinitely
        }else if(type===false){
         // clear object
         this.tableData = null;
        }else if(type===undefined){
         console.warn("_tempStoreTableData must be passed an argument");
        }else{
         // type is a number/ms
         setTimeout(dojo.hitch(this, function(){
          this.tableData = null;
         }), type);
        }
       },

       
       _tempAvailability: function(type){
         // caching or clearing availability, depending on the arg
        if(type===true){
         //store indefinitely
         this.availableCurrentlySet = true;
        }else if(type===false){
         // clear object
         this.availableCurrentlySet = false;
        }else if(type===undefined){
         console.warn("_tempAvailability must be passed an argument");
        }else{
         // type is a number/ms
         this.availableCurrentlySet = true;
         setTimeout(dojo.hitch(this, function(){
          this.availableCurrentlySet = false;
         }), type);
        }

        
       },

       
       connectTableKeys: function(){
        // summary:
        //  When a table is in focus, start detecting keys
        //  Mainly checking for the TAB key so user can tab
        //  through a table (blocking the browser's desire to
        //  tab away from teh editor completely)
        if(this.tablesConnected){ return; }
        this.tablesConnected = true;
        var node = (this.editor.iframe) ? this.editor.document : this.editor.editNode;
        this.cnKeyDn = dojo.connect(node, "onkeydown", this, "onKeyDown");
        this.cnKeyUp = dojo.connect(node, "onkeyup", this, "onKeyUp");
        this._myListeners.push(dojo.connect(node, "onkeypress", this, "onKeyUp"));
       },

       
       disconnectTableKeys: function(){
        //console.log("disconnect")
        dojo.disconnect(this.cnKeyDn);
        dojo.disconnect(this.cnKeyUp);
        this.tablesConnected = false;
       },

       
       onKeyDown: function(evt){
        var key = evt.keyCode;
        //console.log(" -> DOWN:", key);
        if(key == 16){ this.shiftKeyDown = true;}
        if(key == 9) {
         var o = this.getTableInfo();
         //console.log("TAB ", o.tdIndex, o);
         // modifying the o.tdIndex in the tableData directly, because we may save it
         // FIXME: tabTo is a global
         o.tdIndex = (this.shiftKeyDown) ? o.tdIndex-1 : tabTo = o.tdIndex+1;
         if(o.tdIndex>=0 && o.tdIndex
          
          this.editor.selectElement(o.tds[o.tdIndex]);

          
          // we know we are still within a table, so block the need
          // to run the method
          this.currentlyAvailable = true;
          this._tempAvailability(true);
          //
          this._tempStoreTableData(true);
          this.stopEvent = true;
         }else{
          //tabbed out of table
          this.stopEvent = false;
          this.onDisplayChanged();
         }
         if(this.stopEvent) {
          dojo.stopEvent(evt);
         }
        }
       },

       
       onKeyUp: function(evt){
        var key = evt.keyCode;
        //console.log(" -> UP:", key)
        if(key == 16){ this.shiftKeyDown = false;}
        if(key == 37 || key == 38 || key == 39 || key == 40 ){
         // user can arrow or tab out of table - need to recheck
         this.onDisplayChanged();
        }
        if(key == 9 && this.stopEvent){ dojo.stopEvent(evt);}
       },

       
       onDisplayChanged: function(){
        //console.log("onDisplayChanged")
        this.currentlyAvailable = false;
        this._tempStoreTableData(false);
        this._tempAvailability(false);
        this.checkAvailable();
       },


       uninitialize: function(editor){
        // summary:
        //  Function to handle cleaning up of connects
        //  and such. It only finally destroys everything once
        //  all 'references' to it have gone. As in all plugins
        //  that called init on it destroyed their refs in their
        //  cleanup calls.
        // editor:
        //  The editor to detach from.
        if(this.editor == editor){
         this.refCount--;
         if(!this.refCount && this.initialized){
          if(this.tablesConnected){
           this.disconnectTableKeys();
          }
          this.initialized = false;
          dojo.forEach(this._myListeners, function(l){
           dojo.disconnect(l);
          });
          delete this._myListeners;
          delete this.editor._tablePluginHandler;
          delete this.editor;
         }
         this.inherited(arguments);
        }
       }
      });


      dojo.declare("dojox.editor.plugins.TablePlugins",
       dijit._editor._Plugin,
       {
        //summary:
        //  A collection of Plugins for inserting and modifying tables in the Editor
        //  See end of this document for all avaiable plugs
        //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
        //
        //  NOT IMPLEMENTED: Not handling cell merge, span or split
        //

        
        iconClassPrefix: "editorIcon",
        useDefaultCommand: false,
        buttonClass: dijit.form.Button,
        commandName:"",
        label:"",
        alwaysAvailable:false,
        undoEnabled:true,

        
        onDisplayChanged: function(withinTable){
         // subscribed to from the global object's publish method
         //
         //console.log("onDisplayChanged", this.commandName);
         if(!this.alwaysAvailable){
          this.available = withinTable;
          this.button.set('disabled', !this.available);
         }
    • returns
      Fixed the bug that this method always returns the same timestamp
    • summary
  • dojox.editor.plugins.TablePlugins.setEditor

    • type
      Function
    • parameters:
      • editor: (typeof )
    • source: [view]
         this.editor = editor;
         this.editor.customUndo = true;
         this.inherited(arguments);
         this._availableTopic = dojo.subscribe(this.editor.id + "_tablePlugins", this, "onDisplayChanged");
         this.onEditorLoaded();
    • summary
  • dojox.editor.plugins.TablePlugins.onEditorLoaded

    • type
      Function
    • source: [view]
         if(!this.editor._tablePluginHandler){
          // Create it and init it off the editor. This
          // will create the _tablePluginHandler reference on
          // the dijit.Editor instance. This avoids a global.
          var tablePluginHandler = new dojox.editor.plugins._TableHandler();
          tablePluginHandler.initialize(this.editor);
         }else{
          this.editor._tablePluginHandler.initialize(this.editor);
         }
    • summary
  • dojox.editor.plugins.TablePlugins.selectTable

    • type
      Function
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });


       },
       initialize: function(editor){
        // summary:
        //  Initialize the global handler upon a plugin's first instance of setEditor
        //

        
        // All plugins will attempt initialization. We only need to do so once.
        // But keep track so that it is cleaned up when all usage of it for an editor has
        // been removed.
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
       },

       
       getTableInfo: function(forceNewData){
        // summary:
        //  Gets the table in focus
        //  Collects info on the table - see return params
        //
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
       },

       
       connectDraggable: function(){
        // summary:
        //  Detects drag-n-drop in the editor (could probably be moved to there)
        //  Currently only checks if item dragged was a TABLE, and removes its align attr
        //  DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
        //
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
       },
       onDragStart: function(){
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
       },
       onDragEnd: function(){
        // summary:
        //  Detects that an object has been dragged into place
        //  Currently, this code is only used for when a table is dragged
        //  and clears the "align" attribute, so that the table will look
        //  to be more in the place that the user expected.
        //  TODO: This code can be used for other things, most
        //  notably UNDO, which currently is not quite usable.
        //  This code could also find itself in the Editor code when it is
        //  complete.

        
        //console.log("onDragEnd");
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
       },
       checkAvailable: function(){
        // summary:
        //  For table plugs
        //  Checking if a table or part of a table has focus so that
        //  Plugs can change their status
        //
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
       },

       
       _prepareTable: function(tbl){
        // For IE's sake, we are adding IDs to the TDs if none is there
        // We go ahead and use it for other code for convenience
        //
        var tds = this.editor.query("td", tbl);
        console.log("prep:", tds, tbl);
        if(!tds[0].id){
         tds.forEach(function(td, i){
          if(!td.id){
           td.id = "tdid"+i+this.getTimeStamp();
          }
         }, this);
        }
        return tds;
       },

       
       getTimeStamp: function(){
        return new Date().getTime(); // Fixed the bug that this method always returns the same timestamp
      //  return Math.floor(new Date().getTime() * 0.00000001);
       },

       
       _tempStoreTableData: function(type){
        // caching or clearing table data, depending on the arg
        //
        if(type===true){
         //store indefinitely
        }else if(type===false){
         // clear object
         this.tableData = null;
        }else if(type===undefined){
         console.warn("_tempStoreTableData must be passed an argument");
        }else{
         // type is a number/ms
         setTimeout(dojo.hitch(this, function(){
          this.tableData = null;
         }), type);
        }
       },

       
       _tempAvailability: function(type){
         // caching or clearing availability, depending on the arg
        if(type===true){
         //store indefinitely
         this.availableCurrentlySet = true;
        }else if(type===false){
         // clear object
         this.availableCurrentlySet = false;
        }else if(type===undefined){
         console.warn("_tempAvailability must be passed an argument");
        }else{
         // type is a number/ms
         this.availableCurrentlySet = true;
         setTimeout(dojo.hitch(this, function(){
          this.availableCurrentlySet = false;
         }), type);
        }

        
       },

       
       connectTableKeys: function(){
        // summary:
        //  When a table is in focus, start detecting keys
        //  Mainly checking for the TAB key so user can tab
        //  through a table (blocking the browser's desire to
        //  tab away from teh editor completely)
        if(this.tablesConnected){ return; }
        this.tablesConnected = true;
        var node = (this.editor.iframe) ? this.editor.document : this.editor.editNode;
        this.cnKeyDn = dojo.connect(node, "onkeydown", this, "onKeyDown");
        this.cnKeyUp = dojo.connect(node, "onkeyup", this, "onKeyUp");
        this._myListeners.push(dojo.connect(node, "onkeypress", this, "onKeyUp"));
       },

       
       disconnectTableKeys: function(){
        //console.log("disconnect")
        dojo.disconnect(this.cnKeyDn);
        dojo.disconnect(this.cnKeyUp);
        this.tablesConnected = false;
       },

       
       onKeyDown: function(evt){
        var key = evt.keyCode;
        //console.log(" -> DOWN:", key);
        if(key == 16){ this.shiftKeyDown = true;}
        if(key == 9) {
         var o = this.getTableInfo();
         //console.log("TAB ", o.tdIndex, o);
         // modifying the o.tdIndex in the tableData directly, because we may save it
         // FIXME: tabTo is a global
         o.tdIndex = (this.shiftKeyDown) ? o.tdIndex-1 : tabTo = o.tdIndex+1;
         if(o.tdIndex>=0 && o.tdIndex
          
          this.editor.selectElement(o.tds[o.tdIndex]);

          
          // we know we are still within a table, so block the need
          // to run the method
          this.currentlyAvailable = true;
          this._tempAvailability(true);
          //
          this._tempStoreTableData(true);
          this.stopEvent = true;
         }else{
          //tabbed out of table
          this.stopEvent = false;
          this.onDisplayChanged();
         }
         if(this.stopEvent) {
          dojo.stopEvent(evt);
         }
        }
       },

       
       onKeyUp: function(evt){
        var key = evt.keyCode;
        //console.log(" -> UP:", key)
        if(key == 16){ this.shiftKeyDown = false;}
        if(key == 37 || key == 38 || key == 39 || key == 40 ){
         // user can arrow or tab out of table - need to recheck
         this.onDisplayChanged();
        }
        if(key == 9 && this.stopEvent){ dojo.stopEvent(evt);}
       },

       
       onDisplayChanged: function(){
        //console.log("onDisplayChanged")
        this.currentlyAvailable = false;
        this._tempStoreTableData(false);
        this._tempAvailability(false);
        this.checkAvailable();
       },


       uninitialize: function(editor){
        // summary:
        //  Function to handle cleaning up of connects
        //  and such. It only finally destroys everything once
        //  all 'references' to it have gone. As in all plugins
        //  that called init on it destroyed their refs in their
        //  cleanup calls.
        // editor:
        //  The editor to detach from.
        if(this.editor == editor){
         this.refCount--;
         if(!this.refCount && this.initialized){
          if(this.tablesConnected){
           this.disconnectTableKeys();
          }
          this.initialized = false;
          dojo.forEach(this._myListeners, function(l){
           dojo.disconnect(l);
          });
          delete this._myListeners;
          delete this.editor._tablePluginHandler;
          delete this.editor;
         }
         this.inherited(arguments);
        }
       }
      });


      dojo.declare("dojox.editor.plugins.TablePlugins",
       dijit._editor._Plugin,
       {
        //summary:
        //  A collection of Plugins for inserting and modifying tables in the Editor
        //  See end of this document for all avaiable plugs
        //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
        //
        //  NOT IMPLEMENTED: Not handling cell merge, span or split
        //

        
        iconClassPrefix: "editorIcon",
        useDefaultCommand: false,
        buttonClass: dijit.form.Button,
        commandName:"",
        label:"",
        alwaysAvailable:false,
        undoEnabled:true,

        
        onDisplayChanged: function(withinTable){
         // subscribed to from the global object's publish method
         //
         //console.log("onDisplayChanged", this.commandName);
         if(!this.alwaysAvailable){
          this.available = withinTable;
          this.button.set('disabled', !this.available);
         }
        },

        
        setEditor: function(editor){
         this.editor = editor;
         this.editor.customUndo = true;
         this.inherited(arguments);
         this._availableTopic = dojo.subscribe(this.editor.id + "_tablePlugins", this, "onDisplayChanged");
         this.onEditorLoaded();
        },
        onEditorLoaded: function(){
         if(!this.editor._tablePluginHandler){
          // Create it and init it off the editor. This
          // will create the _tablePluginHandler reference on
          // the dijit.Editor instance. This avoids a global.
          var tablePluginHandler = new dojox.editor.plugins._TableHandler();
          tablePluginHandler.initialize(this.editor);
         }else{
          this.editor._tablePluginHandler.initialize(this.editor);
         }
        },

        
        selectTable: function(){
         // selects table that is in focus
         var o = this.getTableInfo();
         if(o && o.tbl){
          dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [o.tbl]);
         }
    • returns
      Fixed the bug that this method always returns the same timestamp
    • summary
  • dojox.editor.plugins.TablePlugins._initButton

    • type
      Function
    • source: [view]
         this.command = this.commandName;

         
         this.label = this.editor.commands[this.command] = this._makeTitle(this.command);
         this.inherited(arguments);
         delete this.command;

         
         this.connect(this.button, "onClick", "modTable");

         
         this.onDisplayChanged(false);
    • summary
  • dojox.editor.plugins.TablePlugins.modTable

    • type
      Function
    • parameters:
      • cmd: (typeof )
      • args: (typeof )
    • source: [view]
         this.begEdit();
         var o = this.getTableInfo();
         var sw = (dojo.isString(cmd))?cmd : this.commandName;
         var r, c, i;
         var adjustColWidth = false;
         //console.log("modTable:", sw)


         if(dojo.isIE){
          // IE can lose selections on focus changes, so focus back
          // in order to restore it.
          this.editor.focus();
         }
         switch(sw){
          case "insertTableRowBefore":
           r = o.tbl.insertRow(o.trIndex);
           for(i=0;i      c = r.insertCell(-1);
            c.innerHTML = " ";
           }
           break;
          case "insertTableRowAfter":
           r = o.tbl.insertRow(o.trIndex+1);
           for(i=0;i      c = r.insertCell(-1);
            c.innerHTML = " ";
           }
           break;
          case "insertTableColumnBefore":
           o.trs.forEach(function(r){
            c = r.insertCell(o.colIndex);
            c.innerHTML = " ";
           });
           adjustColWidth = true;
           break;
          case "insertTableColumnAfter":
           o.trs.forEach(function(r){
            c = r.insertCell(o.colIndex+1);
            c.innerHTML = " ";
           });
           adjustColWidth = true;
           break;
          case "deleteTableRow":
           o.tbl.deleteRow(o.trIndex);
           console.log("TableInfo:", this.getTableInfo());
           break;
          case "deleteTableColumn":
           o.trs.forEach(function(tr){
            tr.deleteCell(o.colIndex);
           });
           adjustColWidth = true;
           break;


          case "modifyTable":
           break;
          case "insertTable":
           break;

          
         }
         if(adjustColWidth){
          this.makeColumnsEven();
         }
         this.endEdit();
    • summary
      Where each plugin performs its action
      Note: not using execCommand. In spite of their presence in the
      Editor as query-able plugins, I was not able to find any evidence
      that they are supported (especially in NOT IE). If they are
      supported in other browsers, it may help with the undo problem.
  • dojox.editor.plugins.TablePlugins.begEdit

    • type
      Function
    • source: [view]
         if(this.editor._tablePluginHandler.undoEnabled){
          //console.log("UNDO:", this.editor.customUndo);
          if(this.editor.customUndo){
           this.editor.beginEditing();
          }else{
           this.valBeforeUndo = this.editor.getValue();
           //console.log("VAL:", this.valBeforeUndo);

           
          }
         }
    • summary
  • dojox.editor.plugins.TablePlugins.endEdit

    • type
      Function
    • source: [view]
         if(this.editor._tablePluginHandler.undoEnabled){
          if(this.editor.customUndo){
           this.editor.endEditing();
          }else{
           // This code ALMOST works for undo -
           // It seems to only work for one step
           // back in history however
           var afterUndo = this.editor.getValue();
           //this.editor.execCommand("inserthtml", "

      mike

      ");
           this.editor.setValue(this.valBeforeUndo);
           this.editor.replaceValue(afterUndo);
          }

          
          this.editor.onDisplayChanged();
         }
    • summary
  • dojox.editor.plugins.TablePlugins.makeColumnsEven

    • type
      Function
    • source: [view]
         setTimeout(dojo.hitch(this, function(){
          var o = this.getTableInfo(true);
          var w = Math.floor(100/o.cols);
          o.tds.forEach(function(d){
           dojo.attr(d, "width", w+"%");
          });
         }), 10);
    • summary
      After changing column amount, change widths to
      keep columns even
      
      the timeout helps prevent an occasional snafu
  • dojox.editor.plugins.TablePlugins.getTableInfo

    • type
      Function
    • parameters:
      • forceNewData: (typeof )
    • source: [view]
         return this.editor._tablePluginHandler.getTableInfo(forceNewData);
    • summary
      Gets the table in focus
      Collects info on the table - see return params
  • dojox.editor.plugins.TablePlugins._makeTitle

    • type
      Function
    • parameters:
      • str: (typeof )
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });


       },
       initialize: function(editor){
        // summary:
        //  Initialize the global handler upon a plugin's first instance of setEditor
        //

        
        // All plugins will attempt initialization. We only need to do so once.
        // But keep track so that it is cleaned up when all usage of it for an editor has
        // been removed.
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
       },

       
       getTableInfo: function(forceNewData){
        // summary:
        //  Gets the table in focus
        //  Collects info on the table - see return params
        //
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
       },

       
       connectDraggable: function(){
        // summary:
        //  Detects drag-n-drop in the editor (could probably be moved to there)
        //  Currently only checks if item dragged was a TABLE, and removes its align attr
        //  DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
        //
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
       },
       onDragStart: function(){
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
       },
       onDragEnd: function(){
        // summary:
        //  Detects that an object has been dragged into place
        //  Currently, this code is only used for when a table is dragged
        //  and clears the "align" attribute, so that the table will look
        //  to be more in the place that the user expected.
        //  TODO: This code can be used for other things, most
        //  notably UNDO, which currently is not quite usable.
        //  This code could also find itself in the Editor code when it is
        //  complete.

        
        //console.log("onDragEnd");
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
       },
       checkAvailable: function(){
        // summary:
        //  For table plugs
        //  Checking if a table or part of a table has focus so that
        //  Plugs can change their status
        //
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
       },

       
       _prepareTable: function(tbl){
        // For IE's sake, we are adding IDs to the TDs if none is there
        // We go ahead and use it for other code for convenience
        //
        var tds = this.editor.query("td", tbl);
        console.log("prep:", tds, tbl);
        if(!tds[0].id){
         tds.forEach(function(td, i){
          if(!td.id){
           td.id = "tdid"+i+this.getTimeStamp();
          }
         }, this);
        }
        return tds;
       },

       
       getTimeStamp: function(){
        return new Date().getTime(); // Fixed the bug that this method always returns the same timestamp
      //  return Math.floor(new Date().getTime() * 0.00000001);
       },

       
       _tempStoreTableData: function(type){
        // caching or clearing table data, depending on the arg
        //
        if(type===true){
         //store indefinitely
        }else if(type===false){
         // clear object
         this.tableData = null;
        }else if(type===undefined){
         console.warn("_tempStoreTableData must be passed an argument");
        }else{
         // type is a number/ms
         setTimeout(dojo.hitch(this, function(){
          this.tableData = null;
         }), type);
        }
       },

       
       _tempAvailability: function(type){
         // caching or clearing availability, depending on the arg
        if(type===true){
         //store indefinitely
         this.availableCurrentlySet = true;
        }else if(type===false){
         // clear object
         this.availableCurrentlySet = false;
        }else if(type===undefined){
         console.warn("_tempAvailability must be passed an argument");
        }else{
         // type is a number/ms
         this.availableCurrentlySet = true;
         setTimeout(dojo.hitch(this, function(){
          this.availableCurrentlySet = false;
         }), type);
        }

        
       },

       
       connectTableKeys: function(){
        // summary:
        //  When a table is in focus, start detecting keys
        //  Mainly checking for the TAB key so user can tab
        //  through a table (blocking the browser's desire to
        //  tab away from teh editor completely)
        if(this.tablesConnected){ return; }
        this.tablesConnected = true;
        var node = (this.editor.iframe) ? this.editor.document : this.editor.editNode;
        this.cnKeyDn = dojo.connect(node, "onkeydown", this, "onKeyDown");
        this.cnKeyUp = dojo.connect(node, "onkeyup", this, "onKeyUp");
        this._myListeners.push(dojo.connect(node, "onkeypress", this, "onKeyUp"));
       },

       
       disconnectTableKeys: function(){
        //console.log("disconnect")
        dojo.disconnect(this.cnKeyDn);
        dojo.disconnect(this.cnKeyUp);
        this.tablesConnected = false;
       },

       
       onKeyDown: function(evt){
        var key = evt.keyCode;
        //console.log(" -> DOWN:", key);
        if(key == 16){ this.shiftKeyDown = true;}
        if(key == 9) {
         var o = this.getTableInfo();
         //console.log("TAB ", o.tdIndex, o);
         // modifying the o.tdIndex in the tableData directly, because we may save it
         // FIXME: tabTo is a global
         o.tdIndex = (this.shiftKeyDown) ? o.tdIndex-1 : tabTo = o.tdIndex+1;
         if(o.tdIndex>=0 && o.tdIndex
          
          this.editor.selectElement(o.tds[o.tdIndex]);

          
          // we know we are still within a table, so block the need
          // to run the method
          this.currentlyAvailable = true;
          this._tempAvailability(true);
          //
          this._tempStoreTableData(true);
          this.stopEvent = true;
         }else{
          //tabbed out of table
          this.stopEvent = false;
          this.onDisplayChanged();
         }
         if(this.stopEvent) {
          dojo.stopEvent(evt);
         }
        }
       },

       
       onKeyUp: function(evt){
        var key = evt.keyCode;
        //console.log(" -> UP:", key)
        if(key == 16){ this.shiftKeyDown = false;}
        if(key == 37 || key == 38 || key == 39 || key == 40 ){
         // user can arrow or tab out of table - need to recheck
         this.onDisplayChanged();
        }
        if(key == 9 && this.stopEvent){ dojo.stopEvent(evt);}
       },

       
       onDisplayChanged: function(){
        //console.log("onDisplayChanged")
        this.currentlyAvailable = false;
        this._tempStoreTableData(false);
        this._tempAvailability(false);
        this.checkAvailable();
       },


       uninitialize: function(editor){
        // summary:
        //  Function to handle cleaning up of connects
        //  and such. It only finally destroys everything once
        //  all 'references' to it have gone. As in all plugins
        //  that called init on it destroyed their refs in their
        //  cleanup calls.
        // editor:
        //  The editor to detach from.
        if(this.editor == editor){
         this.refCount--;
         if(!this.refCount && this.initialized){
          if(this.tablesConnected){
           this.disconnectTableKeys();
          }
          this.initialized = false;
          dojo.forEach(this._myListeners, function(l){
           dojo.disconnect(l);
          });
          delete this._myListeners;
          delete this.editor._tablePluginHandler;
          delete this.editor;
         }
         this.inherited(arguments);
        }
       }
      });


      dojo.declare("dojox.editor.plugins.TablePlugins",
       dijit._editor._Plugin,
       {
        //summary:
        //  A collection of Plugins for inserting and modifying tables in the Editor
        //  See end of this document for all avaiable plugs
        //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
        //
        //  NOT IMPLEMENTED: Not handling cell merge, span or split
        //

        
        iconClassPrefix: "editorIcon",
        useDefaultCommand: false,
        buttonClass: dijit.form.Button,
        commandName:"",
        label:"",
        alwaysAvailable:false,
        undoEnabled:true,

        
        onDisplayChanged: function(withinTable){
         // subscribed to from the global object's publish method
         //
         //console.log("onDisplayChanged", this.commandName);
         if(!this.alwaysAvailable){
          this.available = withinTable;
          this.button.set('disabled', !this.available);
         }
        },

        
        setEditor: function(editor){
         this.editor = editor;
         this.editor.customUndo = true;
         this.inherited(arguments);
         this._availableTopic = dojo.subscribe(this.editor.id + "_tablePlugins", this, "onDisplayChanged");
         this.onEditorLoaded();
        },
        onEditorLoaded: function(){
         if(!this.editor._tablePluginHandler){
          // Create it and init it off the editor. This
          // will create the _tablePluginHandler reference on
          // the dijit.Editor instance. This avoids a global.
          var tablePluginHandler = new dojox.editor.plugins._TableHandler();
          tablePluginHandler.initialize(this.editor);
         }else{
          this.editor._tablePluginHandler.initialize(this.editor);
         }
        },

        
        selectTable: function(){
         // selects table that is in focus
         var o = this.getTableInfo();
         if(o && o.tbl){
          dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [o.tbl]);
         }
        },

        
        _initButton: function(){
         this.command = this.commandName;

         
         this.label = this.editor.commands[this.command] = this._makeTitle(this.command);
         this.inherited(arguments);
         delete this.command;

         
         this.connect(this.button, "onClick", "modTable");

         
         this.onDisplayChanged(false);
        },

        
        modTable: function(cmd, args){
         // summary:
         //  Where each plugin performs its action
         //  Note: not using execCommand. In spite of their presence in the
         //  Editor as query-able plugins, I was not able to find any evidence
         //  that they are supported (especially in NOT IE). If they are
         //  supported in other browsers, it may help with the undo problem.
         //
         this.begEdit();
         var o = this.getTableInfo();
         var sw = (dojo.isString(cmd))?cmd : this.commandName;
         var r, c, i;
         var adjustColWidth = false;
         //console.log("modTable:", sw)


         if(dojo.isIE){
          // IE can lose selections on focus changes, so focus back
          // in order to restore it.
          this.editor.focus();
         }
         switch(sw){
          case "insertTableRowBefore":
           r = o.tbl.insertRow(o.trIndex);
           for(i=0;i      c = r.insertCell(-1);
            c.innerHTML = " ";
           }
           break;
          case "insertTableRowAfter":
           r = o.tbl.insertRow(o.trIndex+1);
           for(i=0;i      c = r.insertCell(-1);
            c.innerHTML = " ";
           }
           break;
          case "insertTableColumnBefore":
           o.trs.forEach(function(r){
            c = r.insertCell(o.colIndex);
            c.innerHTML = " ";
           });
           adjustColWidth = true;
           break;
          case "insertTableColumnAfter":
           o.trs.forEach(function(r){
            c = r.insertCell(o.colIndex+1);
            c.innerHTML = " ";
           });
           adjustColWidth = true;
           break;
          case "deleteTableRow":
           o.tbl.deleteRow(o.trIndex);
           console.log("TableInfo:", this.getTableInfo());
           break;
          case "deleteTableColumn":
           o.trs.forEach(function(tr){
            tr.deleteCell(o.colIndex);
           });
           adjustColWidth = true;
           break;


          case "modifyTable":
           break;
          case "insertTable":
           break;

          
         }
         if(adjustColWidth){
          this.makeColumnsEven();
         }
         this.endEdit();
        },

        
        begEdit: function(){
         if(this.editor._tablePluginHandler.undoEnabled){
          //console.log("UNDO:", this.editor.customUndo);
          if(this.editor.customUndo){
           this.editor.beginEditing();
          }else{
           this.valBeforeUndo = this.editor.getValue();
           //console.log("VAL:", this.valBeforeUndo);

           
          }
         }
        },
        endEdit: function(){
         if(this.editor._tablePluginHandler.undoEnabled){
          if(this.editor.customUndo){
           this.editor.endEditing();
          }else{
           // This code ALMOST works for undo -
           // It seems to only work for one step
           // back in history however
           var afterUndo = this.editor.getValue();
           //this.editor.execCommand("inserthtml", "

      mike

      ");
           this.editor.setValue(this.valBeforeUndo);
           this.editor.replaceValue(afterUndo);
          }

          
          this.editor.onDisplayChanged();
         }
        },

        
        makeColumnsEven: function(){
         //summary:
         //  After changing column amount, change widths to
         //  keep columns even
         //
         // the timeout helps prevent an occasional snafu
         setTimeout(dojo.hitch(this, function(){
          var o = this.getTableInfo(true);
          var w = Math.floor(100/o.cols);
          o.tds.forEach(function(d){
           dojo.attr(d, "width", w+"%");
          });
         }), 10);
        },

        
        getTableInfo: function(forceNewData){
         // summary:
         //  Gets the table in focus
         //  Collects info on the table - see return params
         //
         return this.editor._tablePluginHandler.getTableInfo(forceNewData);
        },
        _makeTitle: function(str){
         // Parses the commandName into a Title
         // based on camelCase
         var ns = [];
         dojo.forEach(str, function(c, i){
          if(c.charCodeAt(0)<91 && i>0 && ns[i-1].charCodeAt(0)!=32){
           ns.push(" ");
          }
          if(i===0){ c = c.toUpperCase();}
          ns.push(c);
         });
         return ns.join("");
    • returns
      Fixed the bug that this method always returns the same timestamp
    • summary
  • dojox.editor.plugins.TablePlugins.getSelectedCells

    • type
      Function
    • source: [view]
         var cells = [];
         var tbl = this.getTableInfo().tbl;
         this.editor._tablePluginHandler._prepareTable(tbl);
         var e = this.editor;


         // Lets do this the way IE originally was (Looking up ids). Walking the selection
         // is inconsistent in the browsers (and painful), so going by ids is simpler.
         var text = dojo.withGlobal(e.window, "getSelectedHtml",dijit._editor.selection, [null]);
         var str = text.match(/id="*\w*"*/g);
         dojo.forEach(str, function(a){
          var id = a.substring(3, a.length);
          if(id.charAt(0) == "\"" && id.charAt(id.length - 1) == "\""){
           id = id.substring(1, id.length - 1);
          }
          var node = e.byId(id);
          if(node && node.tagName.toLowerCase() == "td"){
           cells.push(node);
          }
         }, this);


         if(!cells.length){
          //May just be in a cell (cursor point, or selection in a cell), so look upwards.
          //for a cell container.
          var sel = dijit.range.getSelection(e.window);
          if(sel.rangeCount){
           var r = sel.getRangeAt(0);
           var node = r.startContainer;
           while(node && node != e.editNode && node != e.document){
            if(node.nodeType === 1){
             var tg = node.tagName ? node.tagName.toLowerCase() : "";
             if(tg === "td"){
              return [node];
             }
            }
            node = node.parentNode;
           }
          }
         }
         return cells;
    • summary
      Gets the selected cells from the passed table
      Returns: array of TDs or empty array
  • dojox.editor.plugins.TablePlugins.updateState

    • type
      Function
    • source: [view]
         if(this.button){
          if((this.available || this.alwaysAvailable) && !this.get("disabled")){
           this.button.set("disabled",false);
          }else{
           this.button.set("disabled",true);
          }
         }
    • summary
      Over-ride for button state control for disabled to work.
  • dojox.editor.plugins.TablePlugins.destroy

    • type
      Function
    • source: [view]
         this.inherited(arguments);
         dojo.unsubscribe(this._availableTopic);


         // Disconnect the editor from the handler
         // to clean up refs. Moved to using a per-editor
         // 'handler' to avoid collisions on the old global.
         this.editor._tablePluginHandler.uninitialize(this.editor);
    • summary
      Over-ridden destroy to do some cleanup.
  • dojox.editor.plugins.TablePlugins.available

    • summary
  • dojox.editor.plugins.TablePlugins.editor

    • summary
  • dojox.editor.plugins.TablePlugins.editor.customUndo

    • summary
  • dojox.editor.plugins.TablePlugins._availableTopic

    • summary
  • dojox.editor.plugins.TablePlugins.command

    • summary
  • dojox.editor.plugins.TablePlugins.valBeforeUndo

    • summary
  • dojox.editor.plugins.TableContextMenu

    • type
      Function
    • chains:
      • dojox.editor.plugins.TablePlugins: (prototype)
      • dojox.editor.plugins.TablePlugins: (call)
    • source: [view]
         this.connect(this, "setEditor", function(editor){
          editor.onLoadDeferred.addCallback(dojo.hitch(this, function() {
           this._createContextMenu();
          }));
          this.button.domNode.style.display = "none";
         });
    • summary
      Initialize certain plugins
  • dojox.editor.plugins.TableContextMenu.destroy

    • type
      Function
    • source: [view]
         if(this.menu){
          this.menu.destroyRecursive();
          delete this.menu;
         }
         this.inherited(arguments);
    • summary
      Over-ride to do menu cleanup.
  • dojox.editor.plugins.TableContextMenu._initButton

    • type
      Function
    • source: [view]
         this.inherited(arguments);
         if(this.commandName=="tableContextMenu"){ this.button.domNode.display = "none";}
    • summary
  • dojox.editor.plugins.TableContextMenu._createContextMenu

    • type
      Function
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });


       },
       initialize: function(editor){
        // summary:
        //  Initialize the global handler upon a plugin's first instance of setEditor
        //

        
        // All plugins will attempt initialization. We only need to do so once.
        // But keep track so that it is cleaned up when all usage of it for an editor has
        // been removed.
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
       },

       
       getTableInfo: function(forceNewData){
        // summary:
        //  Gets the table in focus
        //  Collects info on the table - see return params
        //
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
       },

       
       connectDraggable: function(){
        // summary:
        //  Detects drag-n-drop in the editor (could probably be moved to there)
        //  Currently only checks if item dragged was a TABLE, and removes its align attr
        //  DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
        //
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
       },
       onDragStart: function(){
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
       },
       onDragEnd: function(){
        // summary:
        //  Detects that an object has been dragged into place
        //  Currently, this code is only used for when a table is dragged
        //  and clears the "align" attribute, so that the table will look
        //  to be more in the place that the user expected.
        //  TODO: This code can be used for other things, most
        //  notably UNDO, which currently is not quite usable.
        //  This code could also find itself in the Editor code when it is
        //  complete.

        
        //console.log("onDragEnd");
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
       },
       checkAvailable: function(){
        // summary:
        //  For table plugs
        //  Checking if a table or part of a table has focus so that
        //  Plugs can change their status
        //
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
       },

       
       _prepareTable: function(tbl){
        // For IE's sake, we are adding IDs to the TDs if none is there
        // We go ahead and use it for other code for convenience
        //
        var tds = this.editor.query("td", tbl);
        console.log("prep:", tds, tbl);
        if(!tds[0].id){
         tds.forEach(function(td, i){
          if(!td.id){
           td.id = "tdid"+i+this.getTimeStamp();
          }
         }, this);
        }
        return tds;
       },

       
       getTimeStamp: function(){
        return new Date().getTime(); // Fixed the bug that this method always returns the same timestamp
      //  return Math.floor(new Date().getTime() * 0.00000001);
       },

       
       _tempStoreTableData: function(type){
        // caching or clearing table data, depending on the arg
        //
        if(type===true){
         //store indefinitely
        }else if(type===false){
         // clear object
         this.tableData = null;
        }else if(type===undefined){
         console.warn("_tempStoreTableData must be passed an argument");
        }else{
         // type is a number/ms
         setTimeout(dojo.hitch(this, function(){
          this.tableData = null;
         }), type);
        }
       },

       
       _tempAvailability: function(type){
         // caching or clearing availability, depending on the arg
        if(type===true){
         //store indefinitely
         this.availableCurrentlySet = true;
        }else if(type===false){
         // clear object
         this.availableCurrentlySet = false;
        }else if(type===undefined){
         console.warn("_tempAvailability must be passed an argument");
        }else{
         // type is a number/ms
         this.availableCurrentlySet = true;
         setTimeout(dojo.hitch(this, function(){
          this.availableCurrentlySet = false;
         }), type);
        }

        
       },

       
       connectTableKeys: function(){
        // summary:
        //  When a table is in focus, start detecting keys
        //  Mainly checking for the TAB key so user can tab
        //  through a table (blocking the browser's desire to
        //  tab away from teh editor completely)
        if(this.tablesConnected){ return; }
        this.tablesConnected = true;
        var node = (this.editor.iframe) ? this.editor.document : this.editor.editNode;
        this.cnKeyDn = dojo.connect(node, "onkeydown", this, "onKeyDown");
        this.cnKeyUp = dojo.connect(node, "onkeyup", this, "onKeyUp");
        this._myListeners.push(dojo.connect(node, "onkeypress", this, "onKeyUp"));
       },

       
       disconnectTableKeys: function(){
        //console.log("disconnect")
        dojo.disconnect(this.cnKeyDn);
        dojo.disconnect(this.cnKeyUp);
        this.tablesConnected = false;
       },

       
       onKeyDown: function(evt){
        var key = evt.keyCode;
        //console.log(" -> DOWN:", key);
        if(key == 16){ this.shiftKeyDown = true;}
        if(key == 9) {
         var o = this.getTableInfo();
         //console.log("TAB ", o.tdIndex, o);
         // modifying the o.tdIndex in the tableData directly, because we may save it
         // FIXME: tabTo is a global
         o.tdIndex = (this.shiftKeyDown) ? o.tdIndex-1 : tabTo = o.tdIndex+1;
         if(o.tdIndex>=0 && o.tdIndex
          
          this.editor.selectElement(o.tds[o.tdIndex]);

          
          // we know we are still within a table, so block the need
          // to run the method
          this.currentlyAvailable = true;
          this._tempAvailability(true);
          //
          this._tempStoreTableData(true);
          this.stopEvent = true;
         }else{
          //tabbed out of table
          this.stopEvent = false;
          this.onDisplayChanged();
         }
         if(this.stopEvent) {
          dojo.stopEvent(evt);
         }
        }
       },

       
       onKeyUp: function(evt){
        var key = evt.keyCode;
        //console.log(" -> UP:", key)
        if(key == 16){ this.shiftKeyDown = false;}
        if(key == 37 || key == 38 || key == 39 || key == 40 ){
         // user can arrow or tab out of table - need to recheck
         this.onDisplayChanged();
        }
        if(key == 9 && this.stopEvent){ dojo.stopEvent(evt);}
       },

       
       onDisplayChanged: function(){
        //console.log("onDisplayChanged")
        this.currentlyAvailable = false;
        this._tempStoreTableData(false);
        this._tempAvailability(false);
        this.checkAvailable();
       },


       uninitialize: function(editor){
        // summary:
        //  Function to handle cleaning up of connects
        //  and such. It only finally destroys everything once
        //  all 'references' to it have gone. As in all plugins
        //  that called init on it destroyed their refs in their
        //  cleanup calls.
        // editor:
        //  The editor to detach from.
        if(this.editor == editor){
         this.refCount--;
         if(!this.refCount && this.initialized){
          if(this.tablesConnected){
           this.disconnectTableKeys();
          }
          this.initialized = false;
          dojo.forEach(this._myListeners, function(l){
           dojo.disconnect(l);
          });
          delete this._myListeners;
          delete this.editor._tablePluginHandler;
          delete this.editor;
         }
         this.inherited(arguments);
        }
       }
      });


      dojo.declare("dojox.editor.plugins.TablePlugins",
       dijit._editor._Plugin,
       {
        //summary:
        //  A collection of Plugins for inserting and modifying tables in the Editor
        //  See end of this document for all avaiable plugs
        //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
        //
        //  NOT IMPLEMENTED: Not handling cell merge, span or split
        //

        
        iconClassPrefix: "editorIcon",
        useDefaultCommand: false,
        buttonClass: dijit.form.Button,
        commandName:"",
        label:"",
        alwaysAvailable:false,
        undoEnabled:true,

        
        onDisplayChanged: function(withinTable){
         // subscribed to from the global object's publish method
         //
         //console.log("onDisplayChanged", this.commandName);
         if(!this.alwaysAvailable){
          this.available = withinTable;
          this.button.set('disabled', !this.available);
         }
        },

        
        setEditor: function(editor){
         this.editor = editor;
         this.editor.customUndo = true;
         this.inherited(arguments);
         this._availableTopic = dojo.subscribe(this.editor.id + "_tablePlugins", this, "onDisplayChanged");
         this.onEditorLoaded();
        },
        onEditorLoaded: function(){
         if(!this.editor._tablePluginHandler){
          // Create it and init it off the editor. This
          // will create the _tablePluginHandler reference on
          // the dijit.Editor instance. This avoids a global.
          var tablePluginHandler = new dojox.editor.plugins._TableHandler();
          tablePluginHandler.initialize(this.editor);
         }else{
          this.editor._tablePluginHandler.initialize(this.editor);
         }
        },

        
        selectTable: function(){
         // selects table that is in focus
         var o = this.getTableInfo();
         if(o && o.tbl){
          dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [o.tbl]);
         }
        },

        
        _initButton: function(){
         this.command = this.commandName;

         
         this.label = this.editor.commands[this.command] = this._makeTitle(this.command);
         this.inherited(arguments);
         delete this.command;

         
         this.connect(this.button, "onClick", "modTable");

         
         this.onDisplayChanged(false);
        },

        
        modTable: function(cmd, args){
         // summary:
         //  Where each plugin performs its action
         //  Note: not using execCommand. In spite of their presence in the
         //  Editor as query-able plugins, I was not able to find any evidence
         //  that they are supported (especially in NOT IE). If they are
         //  supported in other browsers, it may help with the undo problem.
         //
         this.begEdit();
         var o = this.getTableInfo();
         var sw = (dojo.isString(cmd))?cmd : this.commandName;
         var r, c, i;
         var adjustColWidth = false;
         //console.log("modTable:", sw)


         if(dojo.isIE){
          // IE can lose selections on focus changes, so focus back
          // in order to restore it.
          this.editor.focus();
         }
         switch(sw){
          case "insertTableRowBefore":
           r = o.tbl.insertRow(o.trIndex);
           for(i=0;i      c = r.insertCell(-1);
            c.innerHTML = " ";
           }
           break;
          case "insertTableRowAfter":
           r = o.tbl.insertRow(o.trIndex+1);
           for(i=0;i      c = r.insertCell(-1);
            c.innerHTML = " ";
           }
           break;
          case "insertTableColumnBefore":
           o.trs.forEach(function(r){
            c = r.insertCell(o.colIndex);
            c.innerHTML = " ";
           });
           adjustColWidth = true;
           break;
          case "insertTableColumnAfter":
           o.trs.forEach(function(r){
            c = r.insertCell(o.colIndex+1);
            c.innerHTML = " ";
           });
           adjustColWidth = true;
           break;
          case "deleteTableRow":
           o.tbl.deleteRow(o.trIndex);
           console.log("TableInfo:", this.getTableInfo());
           break;
          case "deleteTableColumn":
           o.trs.forEach(function(tr){
            tr.deleteCell(o.colIndex);
           });
           adjustColWidth = true;
           break;


          case "modifyTable":
           break;
          case "insertTable":
           break;

          
         }
         if(adjustColWidth){
          this.makeColumnsEven();
         }
         this.endEdit();
        },

        
        begEdit: function(){
         if(this.editor._tablePluginHandler.undoEnabled){
          //console.log("UNDO:", this.editor.customUndo);
          if(this.editor.customUndo){
           this.editor.beginEditing();
          }else{
           this.valBeforeUndo = this.editor.getValue();
           //console.log("VAL:", this.valBeforeUndo);

           
          }
         }
        },
        endEdit: function(){
         if(this.editor._tablePluginHandler.undoEnabled){
          if(this.editor.customUndo){
           this.editor.endEditing();
          }else{
           // This code ALMOST works for undo -
           // It seems to only work for one step
           // back in history however
           var afterUndo = this.editor.getValue();
           //this.editor.execCommand("inserthtml", "

      mike

      ");
           this.editor.setValue(this.valBeforeUndo);
           this.editor.replaceValue(afterUndo);
          }

          
          this.editor.onDisplayChanged();
         }
        },

        
        makeColumnsEven: function(){
         //summary:
         //  After changing column amount, change widths to
         //  keep columns even
         //
         // the timeout helps prevent an occasional snafu
         setTimeout(dojo.hitch(this, function(){
          var o = this.getTableInfo(true);
          var w = Math.floor(100/o.cols);
          o.tds.forEach(function(d){
           dojo.attr(d, "width", w+"%");
          });
         }), 10);
        },

        
        getTableInfo: function(forceNewData){
         // summary:
         //  Gets the table in focus
         //  Collects info on the table - see return params
         //
         return this.editor._tablePluginHandler.getTableInfo(forceNewData);
        },
        _makeTitle: function(str){
         // Parses the commandName into a Title
         // based on camelCase
         var ns = [];
         dojo.forEach(str, function(c, i){
          if(c.charCodeAt(0)<91 && i>0 && ns[i-1].charCodeAt(0)!=32){
           ns.push(" ");
          }
          if(i===0){ c = c.toUpperCase();}
          ns.push(c);
         });
         return ns.join("");
        },

        

        

        
        getSelectedCells: function(){
         // summary:
         //  Gets the selected cells from the passed table
         //  Returns: array of TDs or empty array
         var cells = [];
         var tbl = this.getTableInfo().tbl;
         this.editor._tablePluginHandler._prepareTable(tbl);
         var e = this.editor;


         // Lets do this the way IE originally was (Looking up ids). Walking the selection
         // is inconsistent in the browsers (and painful), so going by ids is simpler.
         var text = dojo.withGlobal(e.window, "getSelectedHtml",dijit._editor.selection, [null]);
         var str = text.match(/id="*\w*"*/g);
         dojo.forEach(str, function(a){
          var id = a.substring(3, a.length);
          if(id.charAt(0) == "\"" && id.charAt(id.length - 1) == "\""){
           id = id.substring(1, id.length - 1);
          }
          var node = e.byId(id);
          if(node && node.tagName.toLowerCase() == "td"){
           cells.push(node);
          }
         }, this);


         if(!cells.length){
          //May just be in a cell (cursor point, or selection in a cell), so look upwards.
          //for a cell container.
          var sel = dijit.range.getSelection(e.window);
          if(sel.rangeCount){
           var r = sel.getRangeAt(0);
           var node = r.startContainer;
           while(node && node != e.editNode && node != e.document){
            if(node.nodeType === 1){
             var tg = node.tagName ? node.tagName.toLowerCase() : "";
             if(tg === "td"){
              return [node];
             }
            }
            node = node.parentNode;
           }
          }
         }
         return cells;
        },

        
        updateState: function(){
         // summary:
         //  Over-ride for button state control for disabled to work.
         if(this.button){
          if((this.available || this.alwaysAvailable) && !this.get("disabled")){
           this.button.set("disabled",false);
          }else{
           this.button.set("disabled",true);
          }
         }
        },


        destroy: function(){
         // summary:
         //  Over-ridden destroy to do some cleanup.
         this.inherited(arguments);
         dojo.unsubscribe(this._availableTopic);


         // Disconnect the editor from the handler
         // to clean up refs. Moved to using a per-editor
         // 'handler' to avoid collisions on the old global.
         this.editor._tablePluginHandler.uninitialize(this.editor);
        }

        
       }
      );


      dojo.declare("dojox.editor.plugins.TableContextMenu",
       dojox.editor.plugins.TablePlugins,
       {
        constructor: function(){
         // summary:
         //  Initialize certain plugins
         //
         this.connect(this, "setEditor", function(editor){
          editor.onLoadDeferred.addCallback(dojo.hitch(this, function() {
           this._createContextMenu();
          }));
          this.button.domNode.style.display = "none";
         });
        },


        destroy: function(){
         // summary:
         // Over-ride to do menu cleanup.
         if(this.menu){
          this.menu.destroyRecursive();
          delete this.menu;
         }
         this.inherited(arguments);
        },

       

        
        _initButton: function(){
         this.inherited(arguments);
         if(this.commandName=="tableContextMenu"){ this.button.domNode.display = "none";}
        },

        
        _createContextMenu: function(){
         // summary
         //  Building context menu for right-click shortcuts within a table
         //

        
         var pMenu = new dijit.Menu({targetNodeIds:[this.editor.iframe]});
         var messages = dojo.i18n.getLocalization("dojox.editor.plugins", "TableDialog", this.lang);
         pMenu.addChild(new dijit.MenuItem({label: messages.selectTableLabel, onClick: dojo.hitch(this, "selectTable")}));
         pMenu.addChild(new dijit.MenuSeparator());

         
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableRowBeforeLabel, onClick: dojo.hitch(this, "modTable", "insertTableRowBefore" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableRowAfterLabel, onClick: dojo.hitch(this, "modTable", "insertTableRowAfter" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableColumnBeforeLabel, onClick: dojo.hitch(this, "modTable", "insertTableColumnBefore" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableColumnAfterLabel, onClick: dojo.hitch(this, "modTable", "insertTableColumnAfter" )}));
         pMenu.addChild(new dijit.MenuSeparator());
         pMenu.addChild(new dijit.MenuItem({label: messages.deleteTableRowLabel, onClick: dojo.hitch(this, "modTable", "deleteTableRow" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.deleteTableColumnLabel, onClick: dojo.hitch(this, "modTable", "deleteTableColumn" )}));


         this.menu = pMenu;
    • returns
      Fixed the bug that this method always returns the same timestamp
    • summary
  • dojox.editor.plugins.TableContextMenu.commandName

    • summary
  • dojox.editor.plugins.TableContextMenu.menu

    • summary
  • dojox.editor.plugins.TableContextMenu.button.domNode.style.display

    • summary
  • dojox.editor.plugins.InsertTable

    • type
      Function
    • chains:
      • dojox.editor.plugins.TablePlugins: (prototype)
      • dojox.editor.plugins.TablePlugins: (call)
    • summary
  • dojox.editor.plugins.InsertTable.alwaysAvailable

    • summary
  • dojox.editor.plugins.InsertTable.modTable

    • type
      Function
    • source: [view]
         var w = new dojox.editor.plugins.EditorTableDialog({});
         w.show();
         var c = dojo.connect(w, "onBuildTable", this, function(obj){
          dojo.disconnect(c);

          
          var res = this.editor.execCommand('inserthtml', obj.htmlText);

          
          // commenting this line, due to msg below
          //var td = this.editor.query("td", this.editor.byId(obj.id));

          
          //HMMMM.... This throws a security error now. didn't used to.
          //this.editor.selectElement(td);
         });
    • summary
  • dojox.editor.plugins.ModifyTable

    • type
      Function
    • chains:
      • dojox.editor.plugins.TablePlugins: (prototype)
      • dojox.editor.plugins.TablePlugins: (call)
    • summary
  • dojox.editor.plugins.ModifyTable.modTable

    • type
      Function
    • source: [view]
         if (!this.editor._tablePluginHandler.checkAvailable()) {return;}
         var o = this.getTableInfo();
         //console.log("LAUNCH DIALOG");
         var w = new dojox.editor.plugins.EditorModifyTableDialog({table:o.tbl});
         w.show();
         this.connect(w, "onSetTable", function(color){
          // uhm... not sure whats going on here...
          var o = this.getTableInfo();
          //console.log("set color:", color);
          dojo.attr(o.td, "bgcolor", color);
         });
    • summary
  • dojox.editor.plugins._CellColorDropDown

    • type
      Function
    • chains:
      • dijit._Widget: (prototype)
      • dijit._Widget: (call)
      • dijit._Templated: (call)
    • mixins:
      • dijit._Templated.prototype: (prototype)
    • summary
      Constructor over-ride so that the translated strings are mixsed in so
      the template fills out.
    • source: [view]
        var strings = dojo.i18n.getLocalization("dojox.editor.plugins", "TableDialog");
        dojo.mixin(this, strings);
  • dojox.editor.plugins._CellColorDropDown.templateString

    • type
      String
    • summary
      The template used to create the ColorPicker.
  • dojox.editor.plugins._CellColorDropDown.widgetsInTemplate

    • type
      Boolean
    • summary
      Flag denoting widgets are contained in the template.
  • dojox.editor.plugins._CellColorDropDown.startup

    • type
      Function
    • source: [view]
        if(!this._started){
         this.inherited(arguments);
         this.connect(this._setButton, "onClick", function(){
          this.onChange(this.get("value"));
         });
         this.connect(this._cancelButton, "onClick", function(){
          dijit.popup.close(this.dialog);
          this.onCancel();
         });
         // Fully statred, so go ahead and remove the hide.
         dojo.style(this.domNode, "display", "block");
        }
    • summary
      Over-ride of startup to do the basic connect setups and such.
  • dojox.editor.plugins._CellColorDropDown._setValueAttr

    • type
      Function
    • parameters:
      • value: (typeof String)
        The value to set in the color picker
      • priorityChange: (typeof Value)
        to indicate whether or not to trigger an onChange event.
    • source: [view]
        this._colorPicker.set("value", value, priorityChange);
    • summary
      Passthrough function for the color picker value.
  • dojox.editor.plugins._CellColorDropDown._getValueAttr

    • type
      Function
    • source: [view]
        return this._colorPicker.get("value");
    • summary
      Passthrough function for the color picker value.
  • dojox.editor.plugins._CellColorDropDown.setColor

    • type
      Function
    • parameters:
      • color: (typeof String)
    • source: [view]
        this._colorPicker.setColor(color, false);
    • summary
  • dojox.editor.plugins._CellColorDropDown.onChange

    • type
      Function
    • parameters:
      • value: (typeof String)
        The value from the color picker.
    • source: [view]
        // summary:
        //  Hook point to get the value when the color picker value is selected.
        // value: String
        //  The value from the color picker.
    • summary
      Hook point to get the value when the color picker value is selected.
  • dojox.editor.plugins._CellColorDropDown.onCancel

    • type
      Function
    • source: [view]
        // summary:
        //  Hook point to get when the dialog is canceled.
    • summary
      Hook point to get when the dialog is canceled.
  • dojox.editor.plugins.ColorTableCell

    • type
      Function
    • chains:
      • dojox.editor.plugins.TablePlugins: (prototype)
      • dojox.editor.plugins.TablePlugins: (call)
    • source: [view]
         this.closable = true;
         this.buttonClass = dijit.form.DropDownButton;
         var picker = new dojox.editor.plugins._CellColorDropDown();
         dojo.body().appendChild(picker.domNode);
         picker.startup();
         this.dropDown = picker.dialog;
         this.connect(picker, "onChange", function(color){
          this.modTable(null, color);
          this.editor.focus();
         });
         this.connect(picker, "onCancel", function(color){
          this.editor.focus();
         });
         this.connect(picker.dialog, "onOpen", function(){
          var o = this.getTableInfo(),
           tds = this.getSelectedCells(o.tbl);
          if(tds && tds.length > 0){
           var t = tds[0] == this.lastObject ? tds[0] : tds[tds.length - 1],
            color;
           while(t && ((color = dojo.style(t, "backgroundColor")) == "transparent" || color.indexOf("rgba") == 0)){
            t = t.parentNode;
           }
           color = dojo.style(t, "backgroundColor");
           if(color != "transparent" && color.indexOf("rgba") != 0){
            picker.setColor(color);
           }
          }
         });
         this.connect(this, "setEditor", function(editor){
          editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
           this.connect(this.editor.editNode, "onmouseup", function(evt){
            this.lastObject = evt.target;
           });
          }));
         });
    • summary
      Initialize ColorTableCell plugin
  • dojox.editor.plugins.ColorTableCell._initButton

    • type
      Function
    • source: [view]
         this.command = this.commandName;

         
         this.label = this.editor.commands[this.command] = this._makeTitle(this.command);
         this.inherited(arguments);
         delete this.command;


         this.onDisplayChanged(false);
    • summary
  • dojox.editor.plugins.ColorTableCell.modTable

    • type
      Function
    • parameters:
      • cmd: (typeof )
      • args: (typeof )
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });


       },
       initialize: function(editor){
        // summary:
        //  Initialize the global handler upon a plugin's first instance of setEditor
        //

        
        // All plugins will attempt initialization. We only need to do so once.
        // But keep track so that it is cleaned up when all usage of it for an editor has
        // been removed.
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
       },

       
       getTableInfo: function(forceNewData){
        // summary:
        //  Gets the table in focus
        //  Collects info on the table - see return params
        //
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
       },

       
       connectDraggable: function(){
        // summary:
        //  Detects drag-n-drop in the editor (could probably be moved to there)
        //  Currently only checks if item dragged was a TABLE, and removes its align attr
        //  DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
        //
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
       },
       onDragStart: function(){
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
       },
       onDragEnd: function(){
        // summary:
        //  Detects that an object has been dragged into place
        //  Currently, this code is only used for when a table is dragged
        //  and clears the "align" attribute, so that the table will look
        //  to be more in the place that the user expected.
        //  TODO: This code can be used for other things, most
        //  notably UNDO, which currently is not quite usable.
        //  This code could also find itself in the Editor code when it is
        //  complete.

        
        //console.log("onDragEnd");
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
       },
       checkAvailable: function(){
        // summary:
        //  For table plugs
        //  Checking if a table or part of a table has focus so that
        //  Plugs can change their status
        //
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
       },

       
       _prepareTable: function(tbl){
        // For IE's sake, we are adding IDs to the TDs if none is there
        // We go ahead and use it for other code for convenience
        //
        var tds = this.editor.query("td", tbl);
        console.log("prep:", tds, tbl);
        if(!tds[0].id){
         tds.forEach(function(td, i){
          if(!td.id){
           td.id = "tdid"+i+this.getTimeStamp();
          }
         }, this);
        }
        return tds;
       },

       
       getTimeStamp: function(){
        return new Date().getTime(); // Fixed the bug that this method always returns the same timestamp
      //  return Math.floor(new Date().getTime() * 0.00000001);
       },

       
       _tempStoreTableData: function(type){
        // caching or clearing table data, depending on the arg
        //
        if(type===true){
         //store indefinitely
        }else if(type===false){
         // clear object
         this.tableData = null;
        }else if(type===undefined){
         console.warn("_tempStoreTableData must be passed an argument");
        }else{
         // type is a number/ms
         setTimeout(dojo.hitch(this, function(){
          this.tableData = null;
         }), type);
        }
       },

       
       _tempAvailability: function(type){
         // caching or clearing availability, depending on the arg
        if(type===true){
         //store indefinitely
         this.availableCurrentlySet = true;
        }else if(type===false){
         // clear object
         this.availableCurrentlySet = false;
        }else if(type===undefined){
         console.warn("_tempAvailability must be passed an argument");
        }else{
         // type is a number/ms
         this.availableCurrentlySet = true;
         setTimeout(dojo.hitch(this, function(){
          this.availableCurrentlySet = false;
         }), type);
        }

        
       },

       
       connectTableKeys: function(){
        // summary:
        //  When a table is in focus, start detecting keys
        //  Mainly checking for the TAB key so user can tab
        //  through a table (blocking the browser's desire to
        //  tab away from teh editor completely)
        if(this.tablesConnected){ return; }
        this.tablesConnected = true;
        var node = (this.editor.iframe) ? this.editor.document : this.editor.editNode;
        this.cnKeyDn = dojo.connect(node, "onkeydown", this, "onKeyDown");
        this.cnKeyUp = dojo.connect(node, "onkeyup", this, "onKeyUp");
        this._myListeners.push(dojo.connect(node, "onkeypress", this, "onKeyUp"));
       },

       
       disconnectTableKeys: function(){
        //console.log("disconnect")
        dojo.disconnect(this.cnKeyDn);
        dojo.disconnect(this.cnKeyUp);
        this.tablesConnected = false;
       },

       
       onKeyDown: function(evt){
        var key = evt.keyCode;
        //console.log(" -> DOWN:", key);
        if(key == 16){ this.shiftKeyDown = true;}
        if(key == 9) {
         var o = this.getTableInfo();
         //console.log("TAB ", o.tdIndex, o);
         // modifying the o.tdIndex in the tableData directly, because we may save it
         // FIXME: tabTo is a global
         o.tdIndex = (this.shiftKeyDown) ? o.tdIndex-1 : tabTo = o.tdIndex+1;
         if(o.tdIndex>=0 && o.tdIndex
          
          this.editor.selectElement(o.tds[o.tdIndex]);

          
          // we know we are still within a table, so block the need
          // to run the method
          this.currentlyAvailable = true;
          this._tempAvailability(true);
          //
          this._tempStoreTableData(true);
          this.stopEvent = true;
         }else{
          //tabbed out of table
          this.stopEvent = false;
          this.onDisplayChanged();
         }
         if(this.stopEvent) {
          dojo.stopEvent(evt);
         }
        }
       },

       
       onKeyUp: function(evt){
        var key = evt.keyCode;
        //console.log(" -> UP:", key)
        if(key == 16){ this.shiftKeyDown = false;}
        if(key == 37 || key == 38 || key == 39 || key == 40 ){
         // user can arrow or tab out of table - need to recheck
         this.onDisplayChanged();
        }
        if(key == 9 && this.stopEvent){ dojo.stopEvent(evt);}
       },

       
       onDisplayChanged: function(){
        //console.log("onDisplayChanged")
        this.currentlyAvailable = false;
        this._tempStoreTableData(false);
        this._tempAvailability(false);
        this.checkAvailable();
       },


       uninitialize: function(editor){
        // summary:
        //  Function to handle cleaning up of connects
        //  and such. It only finally destroys everything once
        //  all 'references' to it have gone. As in all plugins
        //  that called init on it destroyed their refs in their
        //  cleanup calls.
        // editor:
        //  The editor to detach from.
        if(this.editor == editor){
         this.refCount--;
         if(!this.refCount && this.initialized){
          if(this.tablesConnected){
           this.disconnectTableKeys();
          }
          this.initialized = false;
          dojo.forEach(this._myListeners, function(l){
           dojo.disconnect(l);
          });
          delete this._myListeners;
          delete this.editor._tablePluginHandler;
          delete this.editor;
         }
         this.inherited(arguments);
        }
       }
      });


      dojo.declare("dojox.editor.plugins.TablePlugins",
       dijit._editor._Plugin,
       {
        //summary:
        //  A collection of Plugins for inserting and modifying tables in the Editor
        //  See end of this document for all avaiable plugs
        //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
        //
        //  NOT IMPLEMENTED: Not handling cell merge, span or split
        //

        
        iconClassPrefix: "editorIcon",
        useDefaultCommand: false,
        buttonClass: dijit.form.Button,
        commandName:"",
        label:"",
        alwaysAvailable:false,
        undoEnabled:true,

        
        onDisplayChanged: function(withinTable){
         // subscribed to from the global object's publish method
         //
         //console.log("onDisplayChanged", this.commandName);
         if(!this.alwaysAvailable){
          this.available = withinTable;
          this.button.set('disabled', !this.available);
         }
        },

        
        setEditor: function(editor){
         this.editor = editor;
         this.editor.customUndo = true;
         this.inherited(arguments);
         this._availableTopic = dojo.subscribe(this.editor.id + "_tablePlugins", this, "onDisplayChanged");
         this.onEditorLoaded();
        },
        onEditorLoaded: function(){
         if(!this.editor._tablePluginHandler){
          // Create it and init it off the editor. This
          // will create the _tablePluginHandler reference on
          // the dijit.Editor instance. This avoids a global.
          var tablePluginHandler = new dojox.editor.plugins._TableHandler();
          tablePluginHandler.initialize(this.editor);
         }else{
          this.editor._tablePluginHandler.initialize(this.editor);
         }
        },

        
        selectTable: function(){
         // selects table that is in focus
         var o = this.getTableInfo();
         if(o && o.tbl){
          dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [o.tbl]);
         }
        },

        
        _initButton: function(){
         this.command = this.commandName;

         
         this.label = this.editor.commands[this.command] = this._makeTitle(this.command);
         this.inherited(arguments);
         delete this.command;

         
         this.connect(this.button, "onClick", "modTable");

         
         this.onDisplayChanged(false);
        },

        
        modTable: function(cmd, args){
         // summary:
         //  Where each plugin performs its action
         //  Note: not using execCommand. In spite of their presence in the
         //  Editor as query-able plugins, I was not able to find any evidence
         //  that they are supported (especially in NOT IE). If they are
         //  supported in other browsers, it may help with the undo problem.
         //
         this.begEdit();
         var o = this.getTableInfo();
         var sw = (dojo.isString(cmd))?cmd : this.commandName;
         var r, c, i;
         var adjustColWidth = false;
         //console.log("modTable:", sw)


         if(dojo.isIE){
          // IE can lose selections on focus changes, so focus back
          // in order to restore it.
          this.editor.focus();
         }
         switch(sw){
          case "insertTableRowBefore":
           r = o.tbl.insertRow(o.trIndex);
           for(i=0;i      c = r.insertCell(-1);
            c.innerHTML = " ";
           }
           break;
          case "insertTableRowAfter":
           r = o.tbl.insertRow(o.trIndex+1);
           for(i=0;i      c = r.insertCell(-1);
            c.innerHTML = " ";
           }
           break;
          case "insertTableColumnBefore":
           o.trs.forEach(function(r){
            c = r.insertCell(o.colIndex);
            c.innerHTML = " ";
           });
           adjustColWidth = true;
           break;
          case "insertTableColumnAfter":
           o.trs.forEach(function(r){
            c = r.insertCell(o.colIndex+1);
            c.innerHTML = " ";
           });
           adjustColWidth = true;
           break;
          case "deleteTableRow":
           o.tbl.deleteRow(o.trIndex);
           console.log("TableInfo:", this.getTableInfo());
           break;
          case "deleteTableColumn":
           o.trs.forEach(function(tr){
            tr.deleteCell(o.colIndex);
           });
           adjustColWidth = true;
           break;


          case "modifyTable":
           break;
          case "insertTable":
           break;

          
         }
         if(adjustColWidth){
          this.makeColumnsEven();
         }
         this.endEdit();
        },

        
        begEdit: function(){
         if(this.editor._tablePluginHandler.undoEnabled){
          //console.log("UNDO:", this.editor.customUndo);
          if(this.editor.customUndo){
           this.editor.beginEditing();
          }else{
           this.valBeforeUndo = this.editor.getValue();
           //console.log("VAL:", this.valBeforeUndo);

           
          }
         }
        },
        endEdit: function(){
         if(this.editor._tablePluginHandler.undoEnabled){
          if(this.editor.customUndo){
           this.editor.endEditing();
          }else{
           // This code ALMOST works for undo -
           // It seems to only work for one step
           // back in history however
           var afterUndo = this.editor.getValue();
           //this.editor.execCommand("inserthtml", "

      mike

      ");
           this.editor.setValue(this.valBeforeUndo);
           this.editor.replaceValue(afterUndo);
          }

          
          this.editor.onDisplayChanged();
         }
        },

        
        makeColumnsEven: function(){
         //summary:
         //  After changing column amount, change widths to
         //  keep columns even
         //
         // the timeout helps prevent an occasional snafu
         setTimeout(dojo.hitch(this, function(){
          var o = this.getTableInfo(true);
          var w = Math.floor(100/o.cols);
          o.tds.forEach(function(d){
           dojo.attr(d, "width", w+"%");
          });
         }), 10);
        },

        
        getTableInfo: function(forceNewData){
         // summary:
         //  Gets the table in focus
         //  Collects info on the table - see return params
         //
         return this.editor._tablePluginHandler.getTableInfo(forceNewData);
        },
        _makeTitle: function(str){
         // Parses the commandName into a Title
         // based on camelCase
         var ns = [];
         dojo.forEach(str, function(c, i){
          if(c.charCodeAt(0)<91 && i>0 && ns[i-1].charCodeAt(0)!=32){
           ns.push(" ");
          }
          if(i===0){ c = c.toUpperCase();}
          ns.push(c);
         });
         return ns.join("");
        },

        

        

        
        getSelectedCells: function(){
         // summary:
         //  Gets the selected cells from the passed table
         //  Returns: array of TDs or empty array
         var cells = [];
         var tbl = this.getTableInfo().tbl;
         this.editor._tablePluginHandler._prepareTable(tbl);
         var e = this.editor;


         // Lets do this the way IE originally was (Looking up ids). Walking the selection
         // is inconsistent in the browsers (and painful), so going by ids is simpler.
         var text = dojo.withGlobal(e.window, "getSelectedHtml",dijit._editor.selection, [null]);
         var str = text.match(/id="*\w*"*/g);
         dojo.forEach(str, function(a){
          var id = a.substring(3, a.length);
          if(id.charAt(0) == "\"" && id.charAt(id.length - 1) == "\""){
           id = id.substring(1, id.length - 1);
          }
          var node = e.byId(id);
          if(node && node.tagName.toLowerCase() == "td"){
           cells.push(node);
          }
         }, this);


         if(!cells.length){
          //May just be in a cell (cursor point, or selection in a cell), so look upwards.
          //for a cell container.
          var sel = dijit.range.getSelection(e.window);
          if(sel.rangeCount){
           var r = sel.getRangeAt(0);
           var node = r.startContainer;
           while(node && node != e.editNode && node != e.document){
            if(node.nodeType === 1){
             var tg = node.tagName ? node.tagName.toLowerCase() : "";
             if(tg === "td"){
              return [node];
             }
            }
            node = node.parentNode;
           }
          }
         }
         return cells;
        },

        
        updateState: function(){
         // summary:
         //  Over-ride for button state control for disabled to work.
         if(this.button){
          if((this.available || this.alwaysAvailable) && !this.get("disabled")){
           this.button.set("disabled",false);
          }else{
           this.button.set("disabled",true);
          }
         }
        },


        destroy: function(){
         // summary:
         //  Over-ridden destroy to do some cleanup.
         this.inherited(arguments);
         dojo.unsubscribe(this._availableTopic);


         // Disconnect the editor from the handler
         // to clean up refs. Moved to using a per-editor
         // 'handler' to avoid collisions on the old global.
         this.editor._tablePluginHandler.uninitialize(this.editor);
        }

        
       }
      );


      dojo.declare("dojox.editor.plugins.TableContextMenu",
       dojox.editor.plugins.TablePlugins,
       {
        constructor: function(){
         // summary:
         //  Initialize certain plugins
         //
         this.connect(this, "setEditor", function(editor){
          editor.onLoadDeferred.addCallback(dojo.hitch(this, function() {
           this._createContextMenu();
          }));
          this.button.domNode.style.display = "none";
         });
        },


        destroy: function(){
         // summary:
         // Over-ride to do menu cleanup.
         if(this.menu){
          this.menu.destroyRecursive();
          delete this.menu;
         }
         this.inherited(arguments);
        },

       

        
        _initButton: function(){
         this.inherited(arguments);
         if(this.commandName=="tableContextMenu"){ this.button.domNode.display = "none";}
        },

        
        _createContextMenu: function(){
         // summary
         //  Building context menu for right-click shortcuts within a table
         //

        
         var pMenu = new dijit.Menu({targetNodeIds:[this.editor.iframe]});
         var messages = dojo.i18n.getLocalization("dojox.editor.plugins", "TableDialog", this.lang);
         pMenu.addChild(new dijit.MenuItem({label: messages.selectTableLabel, onClick: dojo.hitch(this, "selectTable")}));
         pMenu.addChild(new dijit.MenuSeparator());

         
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableRowBeforeLabel, onClick: dojo.hitch(this, "modTable", "insertTableRowBefore" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableRowAfterLabel, onClick: dojo.hitch(this, "modTable", "insertTableRowAfter" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableColumnBeforeLabel, onClick: dojo.hitch(this, "modTable", "insertTableColumnBefore" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableColumnAfterLabel, onClick: dojo.hitch(this, "modTable", "insertTableColumnAfter" )}));
         pMenu.addChild(new dijit.MenuSeparator());
         pMenu.addChild(new dijit.MenuItem({label: messages.deleteTableRowLabel, onClick: dojo.hitch(this, "modTable", "deleteTableRow" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.deleteTableColumnLabel, onClick: dojo.hitch(this, "modTable", "deleteTableColumn" )}));


         this.menu = pMenu;
        }
      });


      dojo.declare("dojox.editor.plugins.InsertTable",
       dojox.editor.plugins.TablePlugins,
       {
        alwaysAvailable: true,

           
        modTable: function(){
         var w = new dojox.editor.plugins.EditorTableDialog({});
         w.show();
         var c = dojo.connect(w, "onBuildTable", this, function(obj){
          dojo.disconnect(c);

          
          var res = this.editor.execCommand('inserthtml', obj.htmlText);

          
          // commenting this line, due to msg below
          //var td = this.editor.query("td", this.editor.byId(obj.id));

          
          //HMMMM.... This throws a security error now. didn't used to.
          //this.editor.selectElement(td);
         });
        }
      });


      dojo.declare("dojox.editor.plugins.ModifyTable",
       dojox.editor.plugins.TablePlugins,
       {
        modTable: function(){
         if (!this.editor._tablePluginHandler.checkAvailable()) {return;}
         var o = this.getTableInfo();
         //console.log("LAUNCH DIALOG");
         var w = new dojox.editor.plugins.EditorModifyTableDialog({table:o.tbl});
         w.show();
         this.connect(w, "onSetTable", function(color){
          // uhm... not sure whats going on here...
          var o = this.getTableInfo();
          //console.log("set color:", color);
          dojo.attr(o.td, "bgcolor", color);
         });
        }
      });


      dojo.declare("dojox.editor.plugins._CellColorDropDown", [dijit._Widget, dijit._Templated], {
       // summary:
       //  A smple widget that uses/creates a dropdown with a dojox.widget.ColorPicker. Also provides
       //  passthroughs to the value of the color picker and convenient hook points.
       // tags:
       //  private


       // templateString: String
       //  The template used to create the ColorPicker.
       templateString:
        "
      " +
         "
      " +
          "
      " +
          "
      " +
           "" +
           " " +
           "" +
          "
      " +
         "
      " +
        "
      ",


       // widgetsInTemplate: Boolean
       //  Flag denoting widgets are contained in the template.
       widgetsInTemplate: true,


       constructor: function(){
        // summary:
        //  Constructor over-ride so that the translated strings are mixsed in so
        //  the template fills out.
        var strings = dojo.i18n.getLocalization("dojox.editor.plugins", "TableDialog");
        dojo.mixin(this, strings);
       },


       startup: function(){
        // summary:
        //  Over-ride of startup to do the basic connect setups and such.
        if(!this._started){
         this.inherited(arguments);
         this.connect(this._setButton, "onClick", function(){
          this.onChange(this.get("value"));
         });
         this.connect(this._cancelButton, "onClick", function(){
          dijit.popup.close(this.dialog);
          this.onCancel();
         });
         // Fully statred, so go ahead and remove the hide.
         dojo.style(this.domNode, "display", "block");
        }
       },


       _setValueAttr: function(value, priorityChange){
        // summary:
        //  Passthrough function for the color picker value.
        // value: String
        //  The value to set in the color picker
        // priorityChange:
        //  Value to indicate whether or not to trigger an onChange event.
        this._colorPicker.set("value", value, priorityChange);
       },


       _getValueAttr: function(){
        // summary:
        //  Passthrough function for the color picker value.
        return this._colorPicker.get("value");
       },


       setColor: function(/*String*/ color){
        this._colorPicker.setColor(color, false);
       },

       
       onChange: function(value){
        // summary:
        //  Hook point to get the value when the color picker value is selected.
        // value: String
        //  The value from the color picker.
       },


       onCancel: function(){
        // summary:
        //  Hook point to get when the dialog is canceled.
       }
      });


      dojo.declare("dojox.editor.plugins.ColorTableCell", dojox.editor.plugins.TablePlugins, {
        constructor: function(){
         // summary:
         //  Initialize ColorTableCell plugin
         this.closable = true;
         this.buttonClass = dijit.form.DropDownButton;
         var picker = new dojox.editor.plugins._CellColorDropDown();
         dojo.body().appendChild(picker.domNode);
         picker.startup();
         this.dropDown = picker.dialog;
         this.connect(picker, "onChange", function(color){
          this.modTable(null, color);
          this.editor.focus();
         });
         this.connect(picker, "onCancel", function(color){
          this.editor.focus();
         });
         this.connect(picker.dialog, "onOpen", function(){
          var o = this.getTableInfo(),
           tds = this.getSelectedCells(o.tbl);
          if(tds && tds.length > 0){
           var t = tds[0] == this.lastObject ? tds[0] : tds[tds.length - 1],
            color;
           while(t && ((color = dojo.style(t, "backgroundColor")) == "transparent" || color.indexOf("rgba") == 0)){
            t = t.parentNode;
           }
           color = dojo.style(t, "backgroundColor");
           if(color != "transparent" && color.indexOf("rgba") != 0){
            picker.setColor(color);
           }
          }
         });
         this.connect(this, "setEditor", function(editor){
          editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
           this.connect(this.editor.editNode, "onmouseup", function(evt){
            this.lastObject = evt.target;
           });
          }));
         });
        },

        
        _initButton: function(){
         this.command = this.commandName;

         
         this.label = this.editor.commands[this.command] = this._makeTitle(this.command);
         this.inherited(arguments);
         delete this.command;


         this.onDisplayChanged(false);
        },


        modTable: function(cmd, args){
         // summary
         // Where each plugin performs its action
         // Note: not using execCommand. In spite of their presence in the
         // Editor as query-able plugins, I was not able to find any evidence
         // that they are supported (especially in NOT IE). If they are
         // supported in other browsers, it may help with the undo problem.
         //
         this.begEdit();
         var o = this.getTableInfo();
         // The one plugin that really needs use of the very verbose
         // getSelectedCells()
         var tds = this.getSelectedCells(o.tbl);
         //console.debug("SELECTED CELLS ", tds , " FOR ", o);
         dojo.forEach(tds, function(td){
          dojo.style(td, "backgroundColor", args);
         });
         this.endEdit();
    • returns
      Fixed the bug that this method always returns the same timestamp
    • summary
  • dojox.editor.plugins.ColorTableCell.command

    • summary
  • dojox.editor.plugins.ColorTableCell.label

    • summary
  • dojox.editor.plugins.ColorTableCell.closable

    • summary
  • dojox.editor.plugins.ColorTableCell.buttonClass

    • summary
  • dojox.editor.plugins.ColorTableCell.dropDown

    • summary
  • dojox.editor.plugins.ColorTableCell.lastObject

    • summary
  • dojox.editor.plugins.EditorTableDialog

    • type
      Function
    • chains:
      • dijit.Dialog: (prototype)
      • dijit.Dialog: (call)
    • summary
      Dialog box with options for table creation
  • dojox.editor.plugins.EditorTableDialog.baseClass

    • summary
  • dojox.editor.plugins.EditorTableDialog.widgetsInTemplate

    • summary
  • dojox.editor.plugins.EditorTableDialog.templateString

    • summary
  • dojox.editor.plugins.EditorTableDialog.postMixInProperties

    • type
      Function
    • source: [view]
        var messages = dojo.i18n.getLocalization("dojox.editor.plugins", "TableDialog", this.lang);
        dojo.mixin(this, messages);
        this.inherited(arguments);
    • summary
  • dojox.editor.plugins.EditorTableDialog.postCreate

    • type
      Function
    • source: [view]
        dojo.addClass(this.domNode, this.baseClass); //FIXME - why isn't Dialog accepting the baseClass?
        this.inherited(arguments);
    • summary
  • dojox.editor.plugins.EditorTableDialog.onInsert

    • type
      Function
    • source: [view]
        console.log("insert");

        
        var rows =  this.selectRow.get("value") || 1,
         cols =  this.selectCol.get("value") || 1,
         width =  this.selectWidth.get("value"),
         widthType = this.selectWidthType.get("value"),
         border = this.selectBorder.get("value"),
         pad =  this.selectPad.get("value"),
         space =  this.selectSpace.get("value"),
         _id =  "tbl_"+(new Date().getTime()),
         t = '\n';

        
        for(var r=0;r   t += '\t\n';
         for(var c=0;c    t += '\t\t\n';
         }
         t += '\t\n';
        }
        t += '
       
      ';

        
        //console.log(t);
        this.onBuildTable({htmlText:t, id:_id});
        var cl = dojo.connect(this, "onHide", function(){
         dojo.disconnect(cl);
         var self = this;
         setTimeout(function(){
          self.destroyRecursive();
         }, 10);
        });
        this.hide();
    • summary
  • dojox.editor.plugins.EditorTableDialog.onCancel

    • type
      Function
    • source: [view]
        var c = dojo.connect(this, "onHide", function(){
         dojo.disconnect(c);
         var self = this;
         setTimeout(function(){
          self.destroyRecursive();
         }, 10);
        });
    • summary
      Function to clean up memory so that the dialog is destroyed
      when closed.
  • dojox.editor.plugins.EditorTableDialog.onBuildTable

    • type
      Function
    • parameters:
      • tableText: (typeof )
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
         }
        });


       },
       initialize: function(editor){
        // summary:
        //  Initialize the global handler upon a plugin's first instance of setEditor
        //

        
        // All plugins will attempt initialization. We only need to do so once.
        // But keep track so that it is cleaned up when all usage of it for an editor has
        // been removed.
        this.refCount++;

        
        // Turn on custom undo for all.
        editor.customUndo = true;


        if(this.initialized){ return; }

        
        this.initialized = true;
        this.editor = editor;


        this.editor._tablePluginHandler = this;

        
        //Editor loads async, can't assume doc is ready yet. So, use the deferred of the
        //editor to init at the right time.
        editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
         this.editorDomNode = this.editor.editNode || this.editor.iframe.document.body.firstChild;

         
         // RichText should have a mouseup connection to recognize drag-selections
         // Example would be selecting multiple table cells
         this._myListeners = [];
         this._myListeners.push(dojo.connect(this.editorDomNode , "mouseup", this.editor, "onClick"));
         this._myListeners.push(dojo.connect(this.editor, "onDisplayChanged", this, "checkAvailable"));
         this._myListeners.push(dojo.connect(this.editor, "onBlur", this, "checkAvailable"));
         this.doMixins();
         this.connectDraggable();
        }));
       },

       
       getTableInfo: function(forceNewData){
        // summary:
        //  Gets the table in focus
        //  Collects info on the table - see return params
        //
        if(forceNewData){ this._tempStoreTableData(false); }
        if(this.tableData){
         // tableData is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("returning current tableData:", this.tableData);
         return this.tableData;
        }
        var tr, trs, td, tds, tbl, cols, tdIndex, trIndex;


        td = this.editor.getAncestorElement("td");
        if(td){ tr = td.parentNode; }

        
        tbl = this.editor.getAncestorElement("table");
        //console.log("td:", td);console.log("tr:", tr);console.log("tbl:", tbl)

        
        tds = dojo.query("td", tbl);
        tds.forEach(function(d, i){
         if(td==d){tdIndex = i;}
        });
        trs = dojo.query("tr", tbl);
        trs.forEach(function(r, i){
         if(tr==r){trIndex = i;}
        });
        cols = tds.length/trs.length;
        var o = {
         tbl:tbl,  // focused table
         td:td,   // focused TD
         tr:tr,   // focused TR
         trs:trs,  // rows
         tds:tds,  // cells
         rows:trs.length,// row amount
         cols:cols,  // column amount
         tdIndex:tdIndex,// index of focused cell
         trIndex:trIndex, // index of focused row
         colIndex:tdIndex%cols
        };
        //console.log("NEW tableData:",o);
        this.tableData = o;
        this._tempStoreTableData(500);
        return this.tableData;
       },

       
       connectDraggable: function(){
        // summary:
        //  Detects drag-n-drop in the editor (could probably be moved to there)
        //  Currently only checks if item dragged was a TABLE, and removes its align attr
        //  DOES NOT WORK IN FF - it could - but FF's drag detection is a monster
        //
        if(!dojo.isIE){
         //console.warn("Drag and Drop is currently only detectable in IE.");
         return;
        }

        
        // IE ONLY
        this.editorDomNode.ondragstart = dojo.hitch(this, "onDragStart");
        this.editorDomNode.ondragend = dojo.hitch(this, "onDragEnd");

        
        //NOTES:
        // FF _ Able to detect the drag-over object (the editor.domNode)
        // Not able to detect an item's ondrag() event
        // Don't know why - I actually got it working when there was an error
        // Something to do with different documents or windows I'm sure
        //
        //console.log("connectDraggable", tbl);
        /*tbl.ondragstart=dojo.hitch(this, "onDragStart");

        
        tbl.addEventListener("dragstart", dojo.hitch(this, "onDragStart"), false);
        tbl.addEventListener("drag", dojo.hitch(this, "onDragStart2"), false);
        tbl.addEventListener("dragend", dojo.hitch(this, "onDragStart3"), false);

       
        dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [tbl]);

        
        tbl.ondragstart = function(){
         //console.log("ondragstart");
        };
        tbl.ondrag = function(){
         alert("drag")
         //console.log("ondrag");
        */
       },
       onDragStart: function(){
        var e = window.event;
        if(!e.srcElement.id){
         e.srcElement.id = "tbl_"+(new Date().getTime());
        }
        //console.log("onDragStart", e.srcElement.id);
       },
       onDragEnd: function(){
        // summary:
        //  Detects that an object has been dragged into place
        //  Currently, this code is only used for when a table is dragged
        //  and clears the "align" attribute, so that the table will look
        //  to be more in the place that the user expected.
        //  TODO: This code can be used for other things, most
        //  notably UNDO, which currently is not quite usable.
        //  This code could also find itself in the Editor code when it is
        //  complete.

        
        //console.log("onDragEnd");
        var e = window.event;
        var node = e.srcElement;
        var id = node.id;
        var win = this.editor.window;
        //console.log("NODE:", node.tagName, node.id, dojo.attr(node, "align"));

        
        // clearing a table's align attr
        // TODO: when ondrag becomes more robust, this code block
        // should move to its own method
        if(node.tagName.toLowerCase()=="table"){
         setTimeout(function(){
          var node = dojo.withGlobal(win, "byId", dojo, [id]);
          dojo.removeAttr(node, "align");
          //console.log("set", node.tagName, dojo.attr(node, "align"))
         }, 100);
        }
       },
       checkAvailable: function(){
        // summary:
        //  For table plugs
        //  Checking if a table or part of a table has focus so that
        //  Plugs can change their status
        //
        if(this.availableCurrentlySet){
         // availableCurrentlySet is set for a short amount of time, so that all
         // plugins get the same return without doing the method over
         //console.log("availableCurrentlySet:", this.availableCurrentlySet, "currentlyAvailable:", this.currentlyAvailable)
         return this.currentlyAvailable;
        }
        //console.log("G - checkAvailable...");

        
        if(!this.editor) {
         //console.log("editor not ready")
         return false;
        }
        if(this.alwaysAvailable) {
         //console.log(" return always available")
         return true;
        }

        
        // Only return avalable if the editor is focused.
        this.currentlyAvailable = this.editor._focused ? this.editor.hasAncestorElement("table") : false;

        
        if(this.currentlyAvailable){
         this.connectTableKeys();
        }else{
         this.disconnectTableKeys();
        }

        
        this._tempAvailability(500);

        
        dojo.publish(this.editor.id + "_tablePlugins", [ this.currentlyAvailable ]);
        return this.currentlyAvailable;
       },

       
       _prepareTable: function(tbl){
        // For IE's sake, we are adding IDs to the TDs if none is there
        // We go ahead and use it for other code for convenience
        //
        var tds = this.editor.query("td", tbl);
        console.log("prep:", tds, tbl);
        if(!tds[0].id){
         tds.forEach(function(td, i){
          if(!td.id){
           td.id = "tdid"+i+this.getTimeStamp();
          }
         }, this);
        }
        return tds;
       },

       
       getTimeStamp: function(){
        return new Date().getTime(); // Fixed the bug that this method always returns the same timestamp
      //  return Math.floor(new Date().getTime() * 0.00000001);
       },

       
       _tempStoreTableData: function(type){
        // caching or clearing table data, depending on the arg
        //
        if(type===true){
         //store indefinitely
        }else if(type===false){
         // clear object
         this.tableData = null;
        }else if(type===undefined){
         console.warn("_tempStoreTableData must be passed an argument");
        }else{
         // type is a number/ms
         setTimeout(dojo.hitch(this, function(){
          this.tableData = null;
         }), type);
        }
       },

       
       _tempAvailability: function(type){
         // caching or clearing availability, depending on the arg
        if(type===true){
         //store indefinitely
         this.availableCurrentlySet = true;
        }else if(type===false){
         // clear object
         this.availableCurrentlySet = false;
        }else if(type===undefined){
         console.warn("_tempAvailability must be passed an argument");
        }else{
         // type is a number/ms
         this.availableCurrentlySet = true;
         setTimeout(dojo.hitch(this, function(){
          this.availableCurrentlySet = false;
         }), type);
        }

        
       },

       
       connectTableKeys: function(){
        // summary:
        //  When a table is in focus, start detecting keys
        //  Mainly checking for the TAB key so user can tab
        //  through a table (blocking the browser's desire to
        //  tab away from teh editor completely)
        if(this.tablesConnected){ return; }
        this.tablesConnected = true;
        var node = (this.editor.iframe) ? this.editor.document : this.editor.editNode;
        this.cnKeyDn = dojo.connect(node, "onkeydown", this, "onKeyDown");
        this.cnKeyUp = dojo.connect(node, "onkeyup", this, "onKeyUp");
        this._myListeners.push(dojo.connect(node, "onkeypress", this, "onKeyUp"));
       },

       
       disconnectTableKeys: function(){
        //console.log("disconnect")
        dojo.disconnect(this.cnKeyDn);
        dojo.disconnect(this.cnKeyUp);
        this.tablesConnected = false;
       },

       
       onKeyDown: function(evt){
        var key = evt.keyCode;
        //console.log(" -> DOWN:", key);
        if(key == 16){ this.shiftKeyDown = true;}
        if(key == 9) {
         var o = this.getTableInfo();
         //console.log("TAB ", o.tdIndex, o);
         // modifying the o.tdIndex in the tableData directly, because we may save it
         // FIXME: tabTo is a global
         o.tdIndex = (this.shiftKeyDown) ? o.tdIndex-1 : tabTo = o.tdIndex+1;
         if(o.tdIndex>=0 && o.tdIndex
          
          this.editor.selectElement(o.tds[o.tdIndex]);

          
          // we know we are still within a table, so block the need
          // to run the method
          this.currentlyAvailable = true;
          this._tempAvailability(true);
          //
          this._tempStoreTableData(true);
          this.stopEvent = true;
         }else{
          //tabbed out of table
          this.stopEvent = false;
          this.onDisplayChanged();
         }
         if(this.stopEvent) {
          dojo.stopEvent(evt);
         }
        }
       },

       
       onKeyUp: function(evt){
        var key = evt.keyCode;
        //console.log(" -> UP:", key)
        if(key == 16){ this.shiftKeyDown = false;}
        if(key == 37 || key == 38 || key == 39 || key == 40 ){
         // user can arrow or tab out of table - need to recheck
         this.onDisplayChanged();
        }
        if(key == 9 && this.stopEvent){ dojo.stopEvent(evt);}
       },

       
       onDisplayChanged: function(){
        //console.log("onDisplayChanged")
        this.currentlyAvailable = false;
        this._tempStoreTableData(false);
        this._tempAvailability(false);
        this.checkAvailable();
       },


       uninitialize: function(editor){
        // summary:
        //  Function to handle cleaning up of connects
        //  and such. It only finally destroys everything once
        //  all 'references' to it have gone. As in all plugins
        //  that called init on it destroyed their refs in their
        //  cleanup calls.
        // editor:
        //  The editor to detach from.
        if(this.editor == editor){
         this.refCount--;
         if(!this.refCount && this.initialized){
          if(this.tablesConnected){
           this.disconnectTableKeys();
          }
          this.initialized = false;
          dojo.forEach(this._myListeners, function(l){
           dojo.disconnect(l);
          });
          delete this._myListeners;
          delete this.editor._tablePluginHandler;
          delete this.editor;
         }
         this.inherited(arguments);
        }
       }
      });


      dojo.declare("dojox.editor.plugins.TablePlugins",
       dijit._editor._Plugin,
       {
        //summary:
        //  A collection of Plugins for inserting and modifying tables in the Editor
        //  See end of this document for all avaiable plugs
        //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
        //
        //  NOT IMPLEMENTED: Not handling cell merge, span or split
        //

        
        iconClassPrefix: "editorIcon",
        useDefaultCommand: false,
        buttonClass: dijit.form.Button,
        commandName:"",
        label:"",
        alwaysAvailable:false,
        undoEnabled:true,

        
        onDisplayChanged: function(withinTable){
         // subscribed to from the global object's publish method
         //
         //console.log("onDisplayChanged", this.commandName);
         if(!this.alwaysAvailable){
          this.available = withinTable;
          this.button.set('disabled', !this.available);
         }
        },

        
        setEditor: function(editor){
         this.editor = editor;
         this.editor.customUndo = true;
         this.inherited(arguments);
         this._availableTopic = dojo.subscribe(this.editor.id + "_tablePlugins", this, "onDisplayChanged");
         this.onEditorLoaded();
        },
        onEditorLoaded: function(){
         if(!this.editor._tablePluginHandler){
          // Create it and init it off the editor. This
          // will create the _tablePluginHandler reference on
          // the dijit.Editor instance. This avoids a global.
          var tablePluginHandler = new dojox.editor.plugins._TableHandler();
          tablePluginHandler.initialize(this.editor);
         }else{
          this.editor._tablePluginHandler.initialize(this.editor);
         }
        },

        
        selectTable: function(){
         // selects table that is in focus
         var o = this.getTableInfo();
         if(o && o.tbl){
          dojo.withGlobal(this.editor.window, "selectElement",dijit._editor.selection, [o.tbl]);
         }
        },

        
        _initButton: function(){
         this.command = this.commandName;

         
         this.label = this.editor.commands[this.command] = this._makeTitle(this.command);
         this.inherited(arguments);
         delete this.command;

         
         this.connect(this.button, "onClick", "modTable");

         
         this.onDisplayChanged(false);
        },

        
        modTable: function(cmd, args){
         // summary:
         //  Where each plugin performs its action
         //  Note: not using execCommand. In spite of their presence in the
         //  Editor as query-able plugins, I was not able to find any evidence
         //  that they are supported (especially in NOT IE). If they are
         //  supported in other browsers, it may help with the undo problem.
         //
         this.begEdit();
         var o = this.getTableInfo();
         var sw = (dojo.isString(cmd))?cmd : this.commandName;
         var r, c, i;
         var adjustColWidth = false;
         //console.log("modTable:", sw)


         if(dojo.isIE){
          // IE can lose selections on focus changes, so focus back
          // in order to restore it.
          this.editor.focus();
         }
         switch(sw){
          case "insertTableRowBefore":
           r = o.tbl.insertRow(o.trIndex);
           for(i=0;i      c = r.insertCell(-1);
            c.innerHTML = " ";
           }
           break;
          case "insertTableRowAfter":
           r = o.tbl.insertRow(o.trIndex+1);
           for(i=0;i      c = r.insertCell(-1);
            c.innerHTML = " ";
           }
           break;
          case "insertTableColumnBefore":
           o.trs.forEach(function(r){
            c = r.insertCell(o.colIndex);
            c.innerHTML = " ";
           });
           adjustColWidth = true;
           break;
          case "insertTableColumnAfter":
           o.trs.forEach(function(r){
            c = r.insertCell(o.colIndex+1);
            c.innerHTML = " ";
           });
           adjustColWidth = true;
           break;
          case "deleteTableRow":
           o.tbl.deleteRow(o.trIndex);
           console.log("TableInfo:", this.getTableInfo());
           break;
          case "deleteTableColumn":
           o.trs.forEach(function(tr){
            tr.deleteCell(o.colIndex);
           });
           adjustColWidth = true;
           break;


          case "modifyTable":
           break;
          case "insertTable":
           break;

          
         }
         if(adjustColWidth){
          this.makeColumnsEven();
         }
         this.endEdit();
        },

        
        begEdit: function(){
         if(this.editor._tablePluginHandler.undoEnabled){
          //console.log("UNDO:", this.editor.customUndo);
          if(this.editor.customUndo){
           this.editor.beginEditing();
          }else{
           this.valBeforeUndo = this.editor.getValue();
           //console.log("VAL:", this.valBeforeUndo);

           
          }
         }
        },
        endEdit: function(){
         if(this.editor._tablePluginHandler.undoEnabled){
          if(this.editor.customUndo){
           this.editor.endEditing();
          }else{
           // This code ALMOST works for undo -
           // It seems to only work for one step
           // back in history however
           var afterUndo = this.editor.getValue();
           //this.editor.execCommand("inserthtml", "

      mike

      ");
           this.editor.setValue(this.valBeforeUndo);
           this.editor.replaceValue(afterUndo);
          }

          
          this.editor.onDisplayChanged();
         }
        },

        
        makeColumnsEven: function(){
         //summary:
         //  After changing column amount, change widths to
         //  keep columns even
         //
         // the timeout helps prevent an occasional snafu
         setTimeout(dojo.hitch(this, function(){
          var o = this.getTableInfo(true);
          var w = Math.floor(100/o.cols);
          o.tds.forEach(function(d){
           dojo.attr(d, "width", w+"%");
          });
         }), 10);
        },

        
        getTableInfo: function(forceNewData){
         // summary:
         //  Gets the table in focus
         //  Collects info on the table - see return params
         //
         return this.editor._tablePluginHandler.getTableInfo(forceNewData);
        },
        _makeTitle: function(str){
         // Parses the commandName into a Title
         // based on camelCase
         var ns = [];
         dojo.forEach(str, function(c, i){
          if(c.charCodeAt(0)<91 && i>0 && ns[i-1].charCodeAt(0)!=32){
           ns.push(" ");
          }
          if(i===0){ c = c.toUpperCase();}
          ns.push(c);
         });
         return ns.join("");
        },

        

        

        
        getSelectedCells: function(){
         // summary:
         //  Gets the selected cells from the passed table
         //  Returns: array of TDs or empty array
         var cells = [];
         var tbl = this.getTableInfo().tbl;
         this.editor._tablePluginHandler._prepareTable(tbl);
         var e = this.editor;


         // Lets do this the way IE originally was (Looking up ids). Walking the selection
         // is inconsistent in the browsers (and painful), so going by ids is simpler.
         var text = dojo.withGlobal(e.window, "getSelectedHtml",dijit._editor.selection, [null]);
         var str = text.match(/id="*\w*"*/g);
         dojo.forEach(str, function(a){
          var id = a.substring(3, a.length);
          if(id.charAt(0) == "\"" && id.charAt(id.length - 1) == "\""){
           id = id.substring(1, id.length - 1);
          }
          var node = e.byId(id);
          if(node && node.tagName.toLowerCase() == "td"){
           cells.push(node);
          }
         }, this);


         if(!cells.length){
          //May just be in a cell (cursor point, or selection in a cell), so look upwards.
          //for a cell container.
          var sel = dijit.range.getSelection(e.window);
          if(sel.rangeCount){
           var r = sel.getRangeAt(0);
           var node = r.startContainer;
           while(node && node != e.editNode && node != e.document){
            if(node.nodeType === 1){
             var tg = node.tagName ? node.tagName.toLowerCase() : "";
             if(tg === "td"){
              return [node];
             }
            }
            node = node.parentNode;
           }
          }
         }
         return cells;
        },

        
        updateState: function(){
         // summary:
         //  Over-ride for button state control for disabled to work.
         if(this.button){
          if((this.available || this.alwaysAvailable) && !this.get("disabled")){
           this.button.set("disabled",false);
          }else{
           this.button.set("disabled",true);
          }
         }
        },


        destroy: function(){
         // summary:
         //  Over-ridden destroy to do some cleanup.
         this.inherited(arguments);
         dojo.unsubscribe(this._availableTopic);


         // Disconnect the editor from the handler
         // to clean up refs. Moved to using a per-editor
         // 'handler' to avoid collisions on the old global.
         this.editor._tablePluginHandler.uninitialize(this.editor);
        }

        
       }
      );


      dojo.declare("dojox.editor.plugins.TableContextMenu",
       dojox.editor.plugins.TablePlugins,
       {
        constructor: function(){
         // summary:
         //  Initialize certain plugins
         //
         this.connect(this, "setEditor", function(editor){
          editor.onLoadDeferred.addCallback(dojo.hitch(this, function() {
           this._createContextMenu();
          }));
          this.button.domNode.style.display = "none";
         });
        },


        destroy: function(){
         // summary:
         // Over-ride to do menu cleanup.
         if(this.menu){
          this.menu.destroyRecursive();
          delete this.menu;
         }
         this.inherited(arguments);
        },

       

        
        _initButton: function(){
         this.inherited(arguments);
         if(this.commandName=="tableContextMenu"){ this.button.domNode.display = "none";}
        },

        
        _createContextMenu: function(){
         // summary
         //  Building context menu for right-click shortcuts within a table
         //

        
         var pMenu = new dijit.Menu({targetNodeIds:[this.editor.iframe]});
         var messages = dojo.i18n.getLocalization("dojox.editor.plugins", "TableDialog", this.lang);
         pMenu.addChild(new dijit.MenuItem({label: messages.selectTableLabel, onClick: dojo.hitch(this, "selectTable")}));
         pMenu.addChild(new dijit.MenuSeparator());

         
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableRowBeforeLabel, onClick: dojo.hitch(this, "modTable", "insertTableRowBefore" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableRowAfterLabel, onClick: dojo.hitch(this, "modTable", "insertTableRowAfter" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableColumnBeforeLabel, onClick: dojo.hitch(this, "modTable", "insertTableColumnBefore" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.insertTableColumnAfterLabel, onClick: dojo.hitch(this, "modTable", "insertTableColumnAfter" )}));
         pMenu.addChild(new dijit.MenuSeparator());
         pMenu.addChild(new dijit.MenuItem({label: messages.deleteTableRowLabel, onClick: dojo.hitch(this, "modTable", "deleteTableRow" )}));
         pMenu.addChild(new dijit.MenuItem({label: messages.deleteTableColumnLabel, onClick: dojo.hitch(this, "modTable", "deleteTableColumn" )}));


         this.menu = pMenu;
        }
      });


      dojo.declare("dojox.editor.plugins.InsertTable",
       dojox.editor.plugins.TablePlugins,
       {
        alwaysAvailable: true,

           
        modTable: function(){
         var w = new dojox.editor.plugins.EditorTableDialog({});
         w.show();
         var c = dojo.connect(w, "onBuildTable", this, function(obj){
          dojo.disconnect(c);

          
          var res = this.editor.execCommand('inserthtml', obj.htmlText);

          
          // commenting this line, due to msg below
          //var td = this.editor.query("td", this.editor.byId(obj.id));

          
          //HMMMM.... This throws a security error now. didn't used to.
          //this.editor.selectElement(td);
         });
        }
      });


      dojo.declare("dojox.editor.plugins.ModifyTable",
       dojox.editor.plugins.TablePlugins,
       {
        modTable: function(){
         if (!this.editor._tablePluginHandler.checkAvailable()) {return;}
         var o = this.getTableInfo();
         //console.log("LAUNCH DIALOG");
         var w = new dojox.editor.plugins.EditorModifyTableDialog({table:o.tbl});
         w.show();
         this.connect(w, "onSetTable", function(color){
          // uhm... not sure whats going on here...
          var o = this.getTableInfo();
          //console.log("set color:", color);
          dojo.attr(o.td, "bgcolor", color);
         });
        }
      });


      dojo.declare("dojox.editor.plugins._CellColorDropDown", [dijit._Widget, dijit._Templated], {
       // summary:
       //  A smple widget that uses/creates a dropdown with a dojox.widget.ColorPicker. Also provides
       //  passthroughs to the value of the color picker and convenient hook points.
       // tags:
       //  private


       // templateString: String
       //  The template used to create the ColorPicker.
       templateString:
        "
      " +
         "
      " +
          "
      " +
          "
      " +
           "" +
           " " +
           "" +
          "
      " +
         "
      " +
        "
      ",


       // widgetsInTemplate: Boolean
       //  Flag denoting widgets are contained in the template.
       widgetsInTemplate: true,


       constructor: function(){
        // summary:
        //  Constructor over-ride so that the translated strings are mixsed in so
        //  the template fills out.
        var strings = dojo.i18n.getLocalization("dojox.editor.plugins", "TableDialog");
        dojo.mixin(this, strings);
       },


       startup: function(){
        // summary:
        //  Over-ride of startup to do the basic connect setups and such.
        if(!this._started){
         this.inherited(arguments);
         this.connect(this._setButton, "onClick", function(){
          this.onChange(this.get("value"));
         });
         this.connect(this._cancelButton, "onClick", function(){
          dijit.popup.close(this.dialog);
          this.onCancel();
         });
         // Fully statred, so go ahead and remove the hide.
         dojo.style(this.domNode, "display", "block");
        }
       },


       _setValueAttr: function(value, priorityChange){
        // summary:
        //  Passthrough function for the color picker value.
        // value: String
        //  The value to set in the color picker
        // priorityChange:
        //  Value to indicate whether or not to trigger an onChange event.
        this._colorPicker.set("value", value, priorityChange);
       },


       _getValueAttr: function(){
        // summary:
        //  Passthrough function for the color picker value.
        return this._colorPicker.get("value");
       },


       setColor: function(/*String*/ color){
        this._colorPicker.setColor(color, false);
       },

       
       onChange: function(value){
        // summary:
        //  Hook point to get the value when the color picker value is selected.
        // value: String
        //  The value from the color picker.
       },


       onCancel: function(){
        // summary:
        //  Hook point to get when the dialog is canceled.
       }
      });


      dojo.declare("dojox.editor.plugins.ColorTableCell", dojox.editor.plugins.TablePlugins, {
        constructor: function(){
         // summary:
         //  Initialize ColorTableCell plugin
         this.closable = true;
         this.buttonClass = dijit.form.DropDownButton;
         var picker = new dojox.editor.plugins._CellColorDropDown();
         dojo.body().appendChild(picker.domNode);
         picker.startup();
         this.dropDown = picker.dialog;
         this.connect(picker, "onChange", function(color){
          this.modTable(null, color);
          this.editor.focus();
         });
         this.connect(picker, "onCancel", function(color){
          this.editor.focus();
         });
         this.connect(picker.dialog, "onOpen", function(){
          var o = this.getTableInfo(),
           tds = this.getSelectedCells(o.tbl);
          if(tds && tds.length > 0){
           var t = tds[0] == this.lastObject ? tds[0] : tds[tds.length - 1],
            color;
           while(t && ((color = dojo.style(t, "backgroundColor")) == "transparent" || color.indexOf("rgba") == 0)){
            t = t.parentNode;
           }
           color = dojo.style(t, "backgroundColor");
           if(color != "transparent" && color.indexOf("rgba") != 0){
            picker.setColor(color);
           }
          }
         });
         this.connect(this, "setEditor", function(editor){
          editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
           this.connect(this.editor.editNode, "onmouseup", function(evt){
            this.lastObject = evt.target;
           });
          }));
         });
        },

        
        _initButton: function(){
         this.command = this.commandName;

         
         this.label = this.editor.commands[this.command] = this._makeTitle(this.command);
         this.inherited(arguments);
         delete this.command;


         this.onDisplayChanged(false);
        },


        modTable: function(cmd, args){
         // summary
         // Where each plugin performs its action
         // Note: not using execCommand. In spite of their presence in the
         // Editor as query-able plugins, I was not able to find any evidence
         // that they are supported (especially in NOT IE). If they are
         // supported in other browsers, it may help with the undo problem.
         //
         this.begEdit();
         var o = this.getTableInfo();
         // The one plugin that really needs use of the very verbose
         // getSelectedCells()
         var tds = this.getSelectedCells(o.tbl);
         //console.debug("SELECTED CELLS ", tds , " FOR ", o);
         dojo.forEach(tds, function(td){
          dojo.style(td, "backgroundColor", args);
         });
         this.endEdit();
        }
      });


      dojo.declare("dojox.editor.plugins.EditorTableDialog", [dijit.Dialog], {
       // summary:
       //  Dialog box with options for table creation
       //
       baseClass:"EditorTableDialog",

          
       widgetsInTemplate:true,
       templateString: dojo.cache("dojox.editor.plugins", "resources/insertTable.html"),


       postMixInProperties: function(){
        var messages = dojo.i18n.getLocalization("dojox.editor.plugins", "TableDialog", this.lang);
        dojo.mixin(this, messages);
        this.inherited(arguments);
       },


       postCreate: function(){
        dojo.addClass(this.domNode, this.baseClass); //FIXME - why isn't Dialog accepting the baseClass?
        this.inherited(arguments);
       },


       onInsert: function(){
        console.log("insert");

        
        var rows =  this.selectRow.get("value") || 1,
         cols =  this.selectCol.get("value") || 1,
         width =  this.selectWidth.get("value"),
         widthType = this.selectWidthType.get("value"),
         border = this.selectBorder.get("value"),
         pad =  this.selectPad.get("value"),
         space =  this.selectSpace.get("value"),
         _id =  "tbl_"+(new Date().getTime()),
         t = '\n';

        
        for(var r=0;r   t += '\t\n';
         for(var c=0;c    t += '\t\t\n';
         }
         t += '\t\n';
        }
        t += '
       
      ';

        
        //console.log(t);
        this.onBuildTable({htmlText:t, id:_id});
        var cl = dojo.connect(this, "onHide", function(){
         dojo.disconnect(cl);
         var self = this;
         setTimeout(function(){
          self.destroyRecursive();
         }, 10);
        });
        this.hide();
       },


       onCancel: function(){
        // summary:
        //  Function to clean up memory so that the dialog is destroyed
        //  when closed.
        var c = dojo.connect(this, "onHide", function(){
         dojo.disconnect(c);
         var self = this;
         setTimeout(function(){
          self.destroyRecursive();
         }, 10);
        });
       },


       onBuildTable: function(tableText){
        //stub
    • returns
      Fixed the bug that this method always returns the same timestamp
    • summary
  • dojox.editor.plugins.EditorModifyTableDialog

    • type
      Function
    • chains:
      • dijit.Dialog: (prototype)
      • dijit.Dialog: (call)
    • summary
      Dialog box with options for editing a table
  • dojox.editor.plugins.EditorModifyTableDialog.baseClass

    • summary
  • dojox.editor.plugins.EditorModifyTableDialog.widgetsInTemplate

    • summary
  • dojox.editor.plugins.EditorModifyTableDialog.table

    • summary
  • dojox.editor.plugins.EditorModifyTableDialog.tableAtts

    • summary
  • this.editor.getAncestorElement

    • type
      Function
    • parameters:
      • tagName: (typeof )
    • source: [view]
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
    • summary
  • this.editor.hasAncestorElement

    • type
      Function
    • parameters:
      • tagName: (typeof )
    • source: [view]
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
    • summary
  • this.editor.selectElement

    • type
      Function
    • parameters:
      • elem: (typeof )
    • source: [view]
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
    • summary
  • this.editor.byId

    • type
      Function
    • parameters:
      • id: (typeof )
    • source: [view]
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
    • summary
  • this.editor.query

    • type
      Function
    • parameters:
      • arg: (typeof )
      • scope: (typeof )
      • returnFirstOnly: (typeof )
    • source: [view]
      define("dojox/editor/plugins/TablePlugins", ["dojo", "dijit", "dojox", "dijit/form/Button", "dijit/Dialog", "dijit/form/TextBox", "dijit/form/FilteringSelect", "dijit/_editor/_Plugin", "dijit/_editor/selection", "dijit/Menu", "dijit/ColorPalette", "dojox/widget/ColorPicker", "dojo/i18n", "i18n!dojox/editor/plugins/nls/TableDialog"], function(dojo, dijit, dojox) {


      dojo.experimental("dojox.editor.plugins.TablePlugins");


      // summary:
      //  A series of plugins that give the Editor the ability to create and edit
      //  HTML tables. See the end of this document for all avaiable plugins
      //  and dojox/editorPlugins/tests/editorTablePlugs.html for an example
      //
      // example:
      //  | 

      //  |  Editor text is here
      //  | 

      //
      // TODO:
      //  Currently not supporting merging or splitting cells
      //
      // FIXME: Undo is very buggy, and therefore unimeplented in all browsers
      //   except IE - which itself has only been lightly tested.
      //
      // FIXME: Selecting multiple table cells in Firefox looks to be impossible.
      //   This affect the 'colorTableCell' plugin. Cells can still be
      //   colored individually or in rows.


      dojo.declare("dojox.editor.plugins._TableHandler", dijit._editor._Plugin,{
       // summary:
       //  A global object that handles common tasks for all the plugins. Since
       //  there are several plugins that are all calling common methods, it's preferable
       //  that they call a centralized location that either has a set variable or a
       //  timeout to only repeat code-heavy calls when necessary.
       //
       tablesConnected:false,
       currentlyAvailable: false,
       alwaysAvailable:false,
       availableCurrentlySet:false,
       initialized:false,
       tableData: null,
       shiftKeyDown:false,
       editorDomNode: null,
       undoEnabled: true, //Using custom undo for all browsers.
       refCount: 0,

       
       doMixins: function(){

        
        dojo.mixin(this.editor,{
         getAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "getAncestorElement",dijit._editor.selection, [tagName]);
         },
         hasAncestorElement: function(tagName){
          return dojo.withGlobal(this.window, "hasAncestorElement",dijit._editor.selection, [tagName]);
         },
         selectElement: function(elem){
          dojo.withGlobal(this.window, "selectElement",dijit._editor.selection, [elem]);
         },
         byId: function(id){
          return dojo.withGlobal(this.window, "byId", dojo, [id]);
         },
         query: function(arg, scope, returnFirstOnly){
          // this shortcut is dubious - not sure scoping is necessary
          var ar = dojo.withGlobal(this.window, "query", dojo, [arg, scope]);
          return (returnFirstOnly) ? ar[0] : ar;
    • summary
  • this

    • mixins:
      • strings: (normal)
      • messages: (normal)
      • messages: (normal)
    • summary
  • cmd

    • summary
  • o.plugin

    • summary
  • dojox.editor.plugins

    • type
      Object
    • summary
  • dojox.editor

    • type
      Object
    • summary
  • dojox

    • type
      Object
    • summary