4 Replies Latest reply: May 24, 2013 12:15 PM by 704899 RSS

    Why APEX change my plugin code?

    704899
      I just want to know why APEX change my plugin after I upload the .js file. Looks like APEX is doing a wrapper of the original code, is this to maintain some kind of compatibility. The plugin used Jquery UI 1.9, but APEX is using Jquery UI 1.8 , anyways, I just want to know why APEX is doing this and is this normal....




      THANKS!!!
        • 1. Re: Why APEX change my plugin code?
          Patrick Wolf-Oracle
          Hi,

          can you please provide a little bit more details or an example on apex.oracle.com where we can see it ourself? What kind of wrapper is added? Are you loading your own jQueryUI 1.9 library version as part of the plug-in? To my best knowledge, we don't modify any plug-in code, but your plug-in JS code might interfere with the JS libraries APEX loads out of the box. See also http://docs.oracle.com/cd/E37097_01/doc/doc.42/e35121/toc.htm#autoId13 and http://docs.oracle.com/cd/E37097_01/doc/doc.42/e35125/app_comp001.htm#BABFIEDI

          Regards
          Patrick
          -----------
          Member of the APEX development team
          My Blog: http://www.inside-oracle-apex.com
          APEX Plug-Ins: http://apex.oracle.com/plugins
          Twitter: http://www.twitter.com/patrickwolf

          Edited by: Patrick Wolf on May 7, 2013 9:35 AM
          • 2. Re: Why APEX change my plugin code?
            704899
            Im using this plugin TreeTable , the original code looks like this:

            Line: -----

            /*
            * jQuery treeTable Plugin 3.0.1
            * http://ludo.cubicphuse.nl/jquery-treetable
            *
            * Copyright 2013, Ludo van den Boom
            * Dual licensed under the MIT or GPL Version 2 licenses.
            */
            (function() {
            var $, Node, Tree, methods;

            $ = jQuery;

            Node = (function() {
            function Node(row, tree, settings) {
            var parentId;

            this.row = row;
            this.tree = tree;
            this.settings = settings;

            // TODO Ensure id/parentId is always a string (not int)
            this.id = this.row.data(this.settings.nodeIdAttr);

            // TODO Move this to a setParentId function?
            parentId = this.row.data(this.settings.parentIdAttr);
            if (parentId != null && parentId !== "") {
            this.parentId = parentId;
            }

            this.treeCell = $(this.row.children(this.settings.columnElType)[this.settings.column]);
            this.expander = $(this.settings.expanderTemplate);
            this.indenter = $(this.settings.indenterTemplate);
            this.children = [];
            this.initialized = false;
            this.treeCell.prepend(this.indenter);
            }

            Node.prototype.addChild = function(child) {
            return this.children.push(child);
            };

            Node.prototype.ancestors = function() {
            var ancestors, node;
            node = this;
            ancestors = [];
            while (node = node.parentNode()) {
            ancestors.push(node);
            }
            return ancestors;
            };

            Node.prototype.collapse = function() {
            this._hideChildren();
            this.row.removeClass("expanded").addClass("collapsed");
            this.expander.attr("title", this.settings.stringExpand);

            if (this.initialized && this.settings.onNodeCollapse != null) {
            this.settings.onNodeCollapse.apply(this);
            }

            return this;
            };

            // TODO destroy: remove event handlers, expander, indenter, etc.

            Node.prototype.expand = function() {
            if (this.initialized && this.settings.onNodeExpand != null) {
            this.settings.onNodeExpand.apply(this);
            }

            this.row.removeClass("collapsed").addClass("expanded");
            this._showChildren();
            this.expander.attr("title", this.settings.stringCollapse);

            return this;
            };

            Node.prototype.expanded = function() {
            return this.row.hasClass("expanded");
            };

            Node.prototype.hide = function() {
            this._hideChildren();
            this.row.hide();
            return this;
            };

            Node.prototype.isBranchNode = function() {
            if(this.children.length > 0 || this.row.data(this.settings.branchAttr) === true) {
            return true;
            } else {
            return false;
            }
            };

            Node.prototype.level = function() {
            return this.ancestors().length;
            };

            Node.prototype.parentNode = function() {
            if (this.parentId != null) {
            return this.tree[this.parentId];
            } else {
            return null;
            }
            };

            Node.prototype.removeChild = function(child) {
            var i = $.inArray(child, this.children);
            return this.children.splice(i, 1)
            };

            Node.prototype.render = function() {
            var handler,
            settings = this.settings,
            target;

            if (settings.expandable === true && this.isBranchNode()) {
            handler = function(e) {
            $(this).parents("table").treetable("node", $(this).parents("tr").data(settings.nodeIdAttr)).toggle();
            return e.preventDefault();
            };

            this.indenter.html(this.expander);
            target = settings.clickableNodeNames === true ? this.treeCell : this.expander;

            target.off("click.treetable").on("click.treetable", handler);
            target.off("keydown.treetable").on("keydown.treetable", function(e) {
            if (e.keyCode == 13) {
            handler.apply(this, [e]);
            }
            });
            }

            if (settings.expandable === true && settings.initialState === "collapsed") {
            this.collapse();
            } else {
            this.expand();
            }

            this.indenter[0].style.paddingLeft = "" + (this.level() * settings.indent) + "px";

            return this;
            };

            Node.prototype.reveal = function() {
            if (this.parentId != null) {
            this.parentNode().reveal();
            }
            return this.expand();
            };

            Node.prototype.setParent = function(node) {
            if (this.parentId != null) {
            this.tree[this.parentId].removeChild(this);
            }
            this.parentId = node.id;
            this.row.data(this.settings.parentIdAttr, node.id);
            return node.addChild(this);
            };

            Node.prototype.show = function() {
            if (!this.initialized) {
            this._initialize();
            }
            this.row.show();
            if (this.expanded()) {
            this._showChildren();
            }
            return this;
            };

            Node.prototype.toggle = function() {
            if (this.expanded()) {
            this.collapse();
            } else {
            this.expand();
            }
            return this;
            };

            Node.prototype._hideChildren = function() {
            var child, i, len, ref, results;
            _ref = this.children;
            _results = [];
            for (_i = 0, len = ref.length; i < len; _i++) {
            child = _ref[_i];
            _results.push(child.hide());
            }
            return _results;
            };

            Node.prototype._initialize = function() {
            this.render();
            if (this.settings.onNodeInitialized != null) {
            this.settings.onNodeInitialized.apply(this);
            }
            return this.initialized = true;
            };

            Node.prototype._showChildren = function() {
            var child, i, len, ref, results;
            _ref = this.children;
            _results = [];
            for (_i = 0, len = ref.length; i < len; _i++) {
            child = _ref[_i];
            _results.push(child.show());
            }
            return _results;
            };

            return Node;
            })();

            Tree = (function() {
            function Tree(table, settings) {
            this.table = table;
            this.settings = settings;
            this.tree = {};

            // Cache the nodes and roots in simple arrays for quick access/iteration
            this.nodes = [];
            this.roots = [];
            }

            Tree.prototype.collapseAll = function() {
            var node, i, len, ref, results;
            _ref = this.nodes;
            _results = [];
            for (_i = 0, len = ref.length; i < len; _i++) {
            node = _ref[_i];
            _results.push(node.collapse());
            }
            return _results;
            };

            Tree.prototype.expandAll = function() {
            var node, i, len, ref, results;
            _ref = this.nodes;
            _results = [];
            for (_i = 0, len = ref.length; i < len; _i++) {
            node = _ref[_i];
            _results.push(node.expand());
            }
            return _results;
            };

            Tree.prototype.loadRows = function(rows) {
            var node, row, i;

            if (rows != null) {
            for (i = 0; i < rows.length; i++) {
            row = $(rows);

            if (row.data(this.settings.nodeIdAttr) != null) {
            node = new Node(row, this.tree, this.settings);
            this.nodes.push(node);
            this.tree[node.id] = node;

            if (node.parentId != null) {
            this.tree[node.parentId].addChild(node);
            } else {
            this.roots.push(node);
            }
            }
            }
            }

            return this;
            };

            Tree.prototype.move = function(node, destination) {
            // Conditions:
            // 1: node should not be inserted as a child of node itself.
            // 2: destination should not be the same as node's current parent (this
            // prevents node from being moved to the same location where it already
            // is).
            // 3: node should not be inserted in a location in a branch if this would
            // result in node being an ancestor of itself.
            if (node !== destination && destination.id !== node.parentId && $.inArray(node, destination.ancestors()) === -1) {
            node.setParent(destination);
            this._moveRows(node, destination);

            // Re-render parentNode if this is its first child node, and therefore
            // doesn't have the expander yet.
            if (node.parentNode().children.length === 1) {
            node.parentNode().render();
            }
            }
            return this;
            };

            Tree.prototype.render = function() {
            var root, i, len, _ref;
            _ref = this.roots;
            for (_i = 0, len = ref.length; i < len; _i++) {
            root = _ref[_i];

            // Naming is confusing (show/render). I do not call render on node from
            // here.
            root.show();
            }
            return this;
            };

            Tree.prototype._moveRows = function(node, destination) {
            var child, i, len, ref, results;
            node.row.insertAfter(destination.row);
            node.render();
            _ref = node.children;
            _results = [];
            for (_i = 0, len = ref.length; i < len; _i++) {
            child = _ref[_i];
            results.push(this.moveRows(child, node));
            }
            return _results;
            };

            Tree.prototype.unloadBranch = function(node) {
            var child, children, i;

            for (i = 0; i < node.children.length; i++) {
            child = node.children[i];

            // Recursively remove all descendants of node
            this.unloadBranch(child);

            // Remove child from DOM (<tr>)
            child.row.remove();

            // Clean up Tree object (so Node objects are GC-ed)
            delete this.tree[child.id];
            this.nodes.splice($.inArray(child, this.nodes), 1)
            }

            // Reset node's collection of children
            node.children = [];

            return this;
            };


            return Tree;
            })();

            // jQuery Plugin
            methods = {
            init: function(options) {
            var settings;

            settings = $.extend({
            branchAttr: "ttBranch",
            clickableNodeNames: false,
            column: 0,
            columnElType: "td", // i.e. 'td', 'th' or 'td,th'
            expandable: false,
            expanderTemplate: "<a href='#'> </a>",
            indent: 19,
            indenterTemplate: "<span class='indenter'></span>",
            initialState: "collapsed",
            nodeIdAttr: "ttId", // maps to data-tt-id
            parentIdAttr: "ttParentId", // maps to data-tt-parent-id
            stringExpand: "Expand",
            stringCollapse: "Collapse",

            // Events
            onInitialized: null,
            onNodeCollapse: null,
            onNodeExpand: null,
            onNodeInitialized: null
            }, options);

            return this.each(function() {
            var el = $(this), tree;

            if (el.data("treetable") === undefined) {
            tree = new Tree(this, settings);
            tree.loadRows(this.rows).render();

            el.addClass("treetable").data("treetable", tree);

            if (settings.onInitialized != null) {
            settings.onInitialized.apply(tree);
            }
            }

            return el;
            });
            },

            destroy: function() {
            return this.each(function() {
            return $(this).removeData("treetable").removeClass("treetable");
            });
            },

            collapseAll: function() {
            this.data("treetable").collapseAll();
            return this;
            },

            collapseNode: function(id) {
            var node = this.data("treetable").tree[id];

            if (node) {
            node.collapse();
            } else {
            throw new Error("Unknown node '" + id + "'");
            }

            return this;
            },

            expandAll: function() {
            this.data("treetable").expandAll();
            return this;
            },

            expandNode: function(id) {
            var node = this.data("treetable").tree[id];

            if (node) {
            node.expand();
            } else {
            throw new Error("Unknown node '" + id + "'");
            }

            return this;
            },

            loadBranch: function(node, rows) {
            var settings = this.data("treetable").settings,
            tree = this.data("treetable").tree;

            rows = $(rows);

            if (node == null) { // Inserting new root nodes
            this.append(rows);
            } else if (node.children.length > 0) {
            rows.insertAfter(node.children[node.children.length-1].row);
            } else {
            rows.insertAfter(node.row);
            }

            this.data("treetable").loadRows(rows);

            // Make sure nodes are properly initialized
            // TODO Review implementation
            rows.each(function() {
            tree[$(this).data(settings.nodeIdAttr)].show();
            });

            return this;
            },

            move: function(nodeId, destinationId) {
            var destination, node;

            node = this.data("treetable").tree[nodeId];
            destination = this.data("treetable").tree[destinationId];
            this.data("treetable").move(node, destination);

            return this;
            },

            node: function(id) {
            return this.data("treetable").tree[id];
            },

            reveal: function(id) {
            var node = this.data("treetable").tree[id];

            if (node) {
            node.reveal();
            } else {
            throw new Error("Unknown node '" + id + "'");
            }

            return this;
            },

            unloadBranch: function(node) {
            this.data("treetable").unloadBranch(node);
            return this;
            }
            };

            $.fn.treetable = function(method) {
            if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
            } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
            } else {
            return $.error("Method " + method + " does not exist on jQuery.treetable");
            }
            };

            // Expose classes to world
            this.TreeTable || (this.TreeTable = {});
            this.TreeTable.Node = Node;
            this.TreeTable.Tree = Tree;
            }).call(this);

            Line: -----

            When I look at this file in APEX, to check why I can't call loadBranch, I notice that APEX code change to this:

            Line: -----

            /*
            * jQuery treeTable Plugin VERSION
            * http://ludo.cubicphuse.nl/jquery-plugins/treeTable/doc/
            *
            * Copyright 2011, Ludo van den Boom
            * Dual licensed under the MIT or GPL Version 2 licenses.
            */
            (function($) {
            // Helps to make options available to all functions
            // TODO: This gives problems when there are both expandable and non-expandable
            // trees on a page. The options shouldn't be global to all these instances!
            var options;
            var defaultPaddingLeft;
            var persistStore;

            $.fn.treeTable = function(opts) {
            options = $.extend({}, $.fn.treeTable.defaults, opts);

            if(options.persist) {
            persistStore = new Persist.Store(options.persistStoreName);
            }

            return this.each(function() {
            $(this).addClass("treeTable").find("tbody tr").each(function() {
            // Skip initialized nodes.
            if (!$(this).hasClass('initialized')) {
            var isRootNode = ($(this)[0].className.search(options.childPrefix) == -1);

            // To optimize performance of indentation, I retrieve the padding-left
            // value of the first root node. This way I only have to call css
            // once.
            if (isRootNode && isNaN(defaultPaddingLeft)) {
            defaultPaddingLeft = parseInt($($(this).children("td")[options.treeColumn]).css('padding-left'), 10);
            }

            // Set child nodes to initial state if we're in expandable mode.
            if(!isRootNode && options.expandable && options.initialState == "collapsed") {
            $(this).addClass('ui-helper-hidden');
            }

            // If we're not in expandable mode, initialize all nodes.
            // If we're in expandable mode, only initialize root nodes.
            if(!options.expandable || isRootNode) {
            initialize($(this));
            }
            }
            });
            });
            };

            $.fn.treeTable.defaults = {
            childPrefix: "child-of-",
            clickableNodeNames: false,
            expandable: true,
            indent: 19,
            initialState: "collapsed",
            onNodeShow: null,
            onNodeHide: null,
            treeColumn: 0,
            persist: false,
            persistStoreName: 'treeTable',
            stringExpand: "Expand",
            stringCollapse: "Collapse"
            };

            //Expand all nodes
            $.fn.expandAll = function() {
            $(this).find("tr").each(function() {
            $(this).expand();
            });
            };

            //Collapse all nodes
            $.fn.collapseAll = function() {
            $(this).find("tr").each(function() {
            $(this).collapse();
            });
            };

            // Recursively hide all node's children in a tree
            $.fn.collapse = function() {
            return this.each(function() {
            $(this).removeClass("expanded").addClass("collapsed");

            if (options.persist) {
            persistNodeState($(this));
            }

            childrenOf($(this)).each(function() {
            if(!$(this).hasClass("collapsed")) {
            $(this).collapse();
            }

            $(this).addClass('ui-helper-hidden');

            if($.isFunction(options.onNodeHide)) {
            options.onNodeHide.call(this);
            }

            });
            });
            };

            // Recursively show all node's children in a tree
            $.fn.expand = function() {
            return this.each(function() {
            $(this).removeClass("collapsed").addClass("expanded");

            if (options.persist) {
            persistNodeState($(this));
            }

            childrenOf($(this)).each(function() {
            initialize($(this));

            if($(this).is(".expanded.parent")) {
            $(this).expand();
            }

            $(this).removeClass('ui-helper-hidden');

            if($.isFunction(options.onNodeShow)) {
            options.onNodeShow.call(this);
            }
            });
            });
            };

            // Reveal a node by expanding all ancestors
            $.fn.reveal = function() {
            $(ancestorsOf($(this)).reverse()).each(function() {
            initialize($(this));
            $(this).expand().show();
            });

            return this;
            };

            // Add an entire branch to destination
            $.fn.appendBranchTo = function(destination) {
            var node = $(this);
            var parent = parentOf(node);

            var ancestorNames = $.map(ancestorsOf($(destination)), function(a) { return a.id; });

            // Conditions:
            // 1: node should not be inserted in a location in a branch if this would
            // result in node being an ancestor of itself.
            // 2: node should not have a parent OR the destination should not be the
            // same as node's current parent (this last condition prevents node
            // from being moved to the same location where it already is).
            // 3: node should not be inserted as a child of node itself.
            if($.inArray(node[0].id, ancestorNames) == -1 && (!parent || (destination.id != parent[0].id)) && destination.id != node[0].id) {
            indent(node, ancestorsOf(node).length * options.indent * -1); // Remove indentation

            if(parent) { node.removeClass(options.childPrefix + parent[0].id); }

            node.addClass(options.childPrefix + destination.id);
            move(node, destination); // Recursively move nodes to new location
            indent(node, ancestorsOf(node).length * options.indent);
            }

            return this;
            };

            // Add reverse() function from JS Arrays
            $.fn.reverse = function() {
            return this.pushStack(this.get().reverse(), arguments);
            };

            // Toggle an entire branch
            $.fn.toggleBranch = function() {
            if($(this).hasClass("collapsed")) {
            $(this).expand();
            } else {
            $(this).collapse();
            }

            return this;
            };

            // === Private functions

            function ancestorsOf(node) {
            var ancestors = [];
            while(node = parentOf(node)) {
            ancestors[ancestors.length] = node[0];
            }
            return ancestors;
            };

            function childrenOf(node) {
            return $(node).siblings("tr." + options.childPrefix + node[0].id);
            };

            function getPaddingLeft(node) {
            var paddingLeft = parseInt(node[0].style.paddingLeft, 10);
            return (isNaN(paddingLeft)) ? defaultPaddingLeft : paddingLeft;
            }

            function indent(node, value) {
            var cell = $(node.children("td")[options.treeColumn]);
            cell[0].style.paddingLeft = getPaddingLeft(cell) + value + "px";

            childrenOf(node).each(function() {
            indent($(this), value);
            });
            };

            function initialize(node) {
            if(!node.hasClass("initialized")) {
            node.addClass("initialized");

            var childNodes = childrenOf(node);

            if(!node.hasClass("parent") && childNodes.length > 0) {
            node.addClass("parent");
            }

            if(node.hasClass("parent")) {
            var cell = $(node.children("td")[options.treeColumn]);
            var padding = getPaddingLeft(cell) + options.indent;

            childNodes.each(function() {
            $(this).children("td")[options.treeColumn].style.paddingLeft = padding + "px";
            });

            if(options.expandable) {
            var newLink = '<a href="# title="' + options.stringExpand + '" style="margin-left: -' + options.indent + 'px; padding-left: ' + options.indent + 'px" class="expander"></a>';

            if(options.clickableNodeNames) {
            cell.wrapInner(newLink);
            } else {
            cell.html(newLink+cell.html());
            }

            $(cell[0].firstChild).click(function() { node.toggleBranch(); return false; }).mousedown(function() { return false; });
            $(cell[0].firstChild).keydown(function(e) { if(e.keyCode == 13) { node.toggleBranch(); return false; }});

            if(options.clickableNodeNames) {
            cell[0].style.cursor = "pointer";
            $(cell).click(function(e) {
            // Don't double-toggle if the click is on the existing expander icon
            if (e.target.className != 'expander') {
            node.toggleBranch();
            }
            });
            }

            if (options.persist && getPersistedNodeState(node)) {
            node.addClass('expanded');
            }

            // Check for a class set explicitly by the user, otherwise set the default class
            if(!(node.hasClass("expanded") || node.hasClass("collapsed"))) {
            node.addClass(options.initialState);
            }

            if(node.hasClass("expanded")) {
            node.expand();
            }
            }
            }
            }
            };

            function move(node, destination) {
            node.insertAfter(destination);
            childrenOf(node).reverse().each(function() { move($(this), node[0]); });
            };

            function parentOf(node) {
            var classNames = node[0].className.split(' ');

            for(var key=0; key<classNames.length; key++) {
            if(classNames[key].match(options.childPrefix)) {
            return $(node).siblings("#" + classNames[key].substring(options.childPrefix.length));
            }
            }

            return null;
            };

            //saving state functions, not critical, so will not generate alerts on error
            function persistNodeState(node) {
            if(node.hasClass('expanded')) {
            try {
            persistStore.set(node.attr('id'), '1');
            } catch (err) {

            }
            } else {
            try {
            persistStore.remove(node.attr('id'));
            } catch (err) {

            }
            }
            }

            function getPersistedNodeState(node) {
            try {
            return persistStore.get(node.attr('id')) == '1';
            } catch (err) {
            return false;
            }
            }
            })(jQuery);



            I already check this like six times, always getting the same result, Im still using JQuery UI 1.8.

            Edited by: user1745687 on May 7, 2013 5:48 AM
            • 3. Re: Why APEX change my plugin code?
              Patrick Wolf-Oracle
              Looks like if you get a different version of your plug-in JS code. I wouldn't know a way how APEX would know an old version of your plug-in code...

              How do you reference the Tree JavaScript file in your APEX plug-in code? Where have you stored the tree Javascript file? As part of the APEX plug-in file store or have you put it onto a web server?

              Can you upload an example of your APEX plug-in onto apex.oracle.com where the problem is demonstrated?

              Regards
              Patrick
              -----------
              Member of the APEX development team
              My Blog: http://www.inside-oracle-apex.com
              APEX Plug-Ins: http://apex.oracle.com/plugins
              Twitter: http://www.twitter.com/patrickwolf
              • 4. Re: Why APEX change my plugin code?
                704899
                different versions of the plug-in! SOLVED, THANKSS!!!