JPolite

jpolite is an AJAX Portal Framework based on jQuery, for creating Netvibes-like web application interfaces.

JPolite screenshot

It is a powerful base for creating complex / customizable websites quickly, by separating content, appearance and behavior authoring. Yet it provides the ease to turning a HTML content block into tabbed or accordion control without extra code.

For example:
A definition list with class=”accordion” will be turned into an Accordion control automatically.
A form with class=”ajaxform1 will be turned into an Ajaxed form.
Any link with class=”thickbox” will be displayed in Thickbox.

Check out for more at http://www.trilancer.com/jpolite.
Track the code progress at http://code.google.com/p/jpolite.

85 comments on “JPolite

    • You’re more than welcome to use it as your homepage, when you do, remember to follow the customization guidelines and minimize the files.

  1. Hi there,

    I am working on a site for a Volunteer organization in Ottawa Canada and I am wondering if you have an example for the Layout Persistence. I see the function but how do you call it. I can handle passing it to the server via an ajax webmethod (asp.net), but would be interested in how I retrieve the layout and then a concept for storing widgets in each pane.

    Thanks,

    Rob

    • Hi, Rob,

      In jpolite.ext.js you’ll find a the following code:

      s.layoutPersistence = [
      function() {
      //This function is used to load layout, from cookie in this example
      //To load from server side, try $.ajax({url:'serverside_layout.php', async:false})
      return window["eval"]("(" + $.cookie('jpolite2layout') + ")")
      },
      function(s) {
      //This function is used to save layout, from cookie in this example
      //To save to server end, try $.ajax({type:'POST', data:s, url:'serverside_layout.php', async:false})
      return $.cookie('jpolite2layout', s);
      }
      ];

    • Yes, you’re right thickbox was once great but haven’t upgrade for quite some time, colorbox is a good alternative đŸ™‚

      • Hiya
        First i agree this is a great project. I just ran across it the other day. I have been messing with v1 as i did not see a v2 till today.
        My question is this as thinkbox is no longer being upgraded.
        And Yosep said he got colorbox to work.
        I was wondering if you could supply the howto for that.
        Or better choice would be imo shadowbox. A howto for that would be my choice. Im a novice so need a bit of detail not 100% detail but a bit Anything will help though.

      • To use ColorBox or ShadowBox, you will need to do the following:

        1. Include associated CSS files atop the index.html and JS files at the bottom but before jpolite.ext.js
        2. In jpolite.ext.js, in function myControls, first insert initialization statements of the library you choose, e.g., Shadowbox.init({skipSetup: true}); for Shadowbox, then add a line after $.addControls to tell Shadow box the DOM selector and method to invoke Shadowbox, e.g., "a[rel^=shadowbox]": [Shadowbox.setup] and any module with links whose rel = shadowbox[xyz] will work

        Note that Shadowbox.init({skipSetup: true}) will be executed once and only once.
        But “a[rel^=shadowbox]”: [Shadowbox.setup] will result in Shadowbox.setup executed upon all links with rel=shadowbox[xyz] in each newly loaded module

      • Yep still like the work you have done.
        Ive managed to get shadowbox up and running with a bit of a problem. Kept it out of the module load from within Jpolite2.
        Im sure i was doing something wrong. But to be honest not everything i think needs to be within Jpolite2. Rather it needs to work with it or within it.
        Im building a site for myself and i am realy thinking im going to use Jpolite2 it has some attractive concepts i like. Mbextruder is a nice add on but stuff from Mb does not play very nice with outher Jquery stuff. Though ive managed to get it working it took quite sometime.
        Check my testsite out. I have a few more things i want to add as concepts before themeing and adding real content.
        I am having one problem on the download section for Jpolite 2 with the licenses showing up as it scrolls the footer seems to be jumping from within Firefox IE8 and Safar work fine. Maybe some hints on how to correct this.
        When the site is ready i sure am glad to add coments to the work you have done. Got any good ideas on how promote your work with something other then a acknoledgement or icon on a page. Anything your looking at actually?

      • I’ve checked your site, the UI feels kinda slow on my laptop, maybe consider removing some features from JPolite like theme switching or so đŸ™‚

        You’re right JPoltie mainly focus on tab/module handling to make a portal UI simply working. Anything unrelated to module/tab handling can be added without touching JPolite code.

        For IE8 issues, I don’t quite noticed similar problems on my laptop, anyway you may remove the fixed footer for I use CSS position:fixed which doesn’t work in IE6 and may probably cause trouble in later IE versions.

        When ur site is up, do drop me a note, and a link like “powerd by JPolite” back to my site would be great đŸ™‚

  2. Hi,

    I am really impressed about jpolite V2. Thanks a bunch.
    Finally something is as simple as it should be.

    We are going to use it as an application backbone.
    Basically making small “portlets” of key application functionality and having them interact.

    I also tried to insert a google gadget in a “portlet”,but could not make it happen. What would be the correct way ?

    cheers,
    petri.

    • Hi, Petri,

      I haven’t tried Google Gadget support yet, I once worked out Netvibes UWA support, you can check out JPolite V1 for demo.

  3. Hi, I left a commecnt yesterday , but it magically disappeared.
    So reposting …

    What would be the correct way to place a Google gadget in a JpoliteV2 window ?

    I managed to get one on iframe, but I’d rather not use iframes if possible.

    Any hints,

    petri.

    • Petri,

      It didn’t actually disappear, just every comment requires manual approval by default, and I didn’t do that in time, sorry đŸ™‚

      To use a Google Gadget, some backend support is required, for AJAX requests must go through the SAME DOMAIN.

      Since JPolite is mostly front-end code, JavaScript is not enough to get a Gadget into non-IFRAME section.

      I’ll look into it and post some simple reference solution FYI., hopefully before Xmas Eve đŸ™‚

      • Most excellent,

        I came to the same conclusion that we need a server-side bridge to pull this off. But imho the inclusion of gadgets makes jpolite much more attractive solution.
        My customers would like to use workspace type of things as well as my custom apps at the same time.

        I figured out the way to add modules dynamically, since my apps do need that functionality.

        function loadnew(t) {
        $.jpolite.Content.addModule({id: “m104”, c: “c4”}, $.jpolite.Nav.getTab($.jpolite.Nav.ct));
        }

        I just hope that dynamically loading and unloading modules (sometimes containing inline scripts) doesn’t screw memory up too bad.
        or too quickly đŸ™‚

        cheers,

        petri.

      • About memory, I’ve made some simple but crazy tests myself and my findings are that Firefox is so far the best overall player, while unsurprisingly, IE is the worst đŸ˜¦

        So I suggest you work under FF at development time and do lots tests under IE, if your system doesn’t break under IE, then I believe it’ll work fine across all others đŸ™‚

  4. Hi,

    Since I am new to jquery and jpoliteV2 I was wondering what happens to inline scripts when they are loaded along with the module that contains them.

    Where do they go … memorywise ?
    They seem to appear in to the global context.
    Do they ever get deleted from there ?
    What if a another module contains a similarly named script block ?

    Is there a way to load the dynamic scripts to a specific object ?
    For instance load them into”this” ,i.e. the module .
    And if so how would I refer to them from
    a) inside the module,
    b) outside ?

    When dynamically loading modules I might come to a situation where I have the same form(module) open twice or more. How do the control Id’s behave in such situation ?
    How do I refer to these multiple similar forms in scripts ? how do they refer to themselves ?

    I think that this topic of memory management,scope,visibility and referrals propably needs a bigger writeup some day, but could you drop some quick pointers on the subject.

    petri.

    • Actually that’s not JPolite specific question, there are lots discussions out there, regarding the scope of variable as well as the meaning of “this” under different situations. Confusing indeed, takes some study and tryouts to clarify.

      Some quick answers to your questions:

      1.Functions embedded in a module will be globally accessible, that they can be called from elsewhere.
      2.Once the module is removed from DOM tree, the functions will still be available, since functions are actually global variables attached to document, function abc() {...} actually equals var abc = function() {...}
      3.If another module should come with a function of the same name, the previous one will be overwritten — this is just like assigning a new value to a variable — To avoid such issues, one solution is to use namespace, i.e., embed functions within a variable, e.g., var myVar = { …;abc: function() {…};…}, and then call myVar.abc(). But as you can see, the naming of variable “myVar” requires some naming governance still :P. To make things simple, you may well define a function naming convention that makes sense to you, and keep it simple. An automatic naming mechanism is possible, but it’ll require some coding convention enforced to module … I’ll do some tests to find a easy reference solution
      4.To “load scripts into a specific object”: you may need to consider whether there will be multiple existences of such objects. If true, assign a class name to the object and rewrite the code in jpolite.ext.js as a jQuery plugin handler and use that class name to trigger the handler as you can see examples in function myControls(). Otherwise, if the object is globally unique throughout, you may well define code inline HTML with event triggers like onclick=”…”
      5.Currently for JPolite2, you won’t find two copies of a same module under a same tab, since module ID is used as reference to modules under a tab where duplication is not allowed. To reference same modules across tabs, there is not any predefined mechanism so far, you may try jQuery finder $(“#formID”). Again I suggest you define common behavior of similar forms as jQuery plugin targeting forms with a specific class name, that when a new module is loaded, JPolite will try to locate form nodes in the module with that class name, and if found, call the plugin to attach desired behavior or properties to found forms.

      A simple example below you can add in jpolite.ext.js:
      //Define a plugin targeting forms
      $.fn.extend({
      FancyForm: function() {
      return this.each(function() {
      $(this).click(funtion(){
      alert("I'm cool!");
      });
      });
      }
      });


      //Enable the plugin on form.fancyform tags
      function myControls(){
      //Assign Controls handlers to selectors
      $.addControls({
      //JPolite native controls, zero arguement
      ".tabs": [$.fn.Tabs],
      ".accordion": [$.fn.Accordion],
      ".maccordion": [$.fn.MAccordion],
      ".jsonform": [$.fn.JsonForm],
      ".menu": [$.fn.SideMenu],

      ".fancyform": [$.fn.FancyForm],

      So whenever a new module is loaded with a form whose class is “fancyform”, $.fn.FancyForm() will be called to attach the click handler to that form.

      Hope that helps. And feel free to drop me a note in case of trouble.

      • Thanks,

        I started reading your source code to get to know the internal structures better and it helped a lot.
        Now I can make almost all the things I wanted myself.

        As a quick fix for multiple loading for same module I did this:
        1. Uncommented the duplicate load preventionblock in AddModule.
        2. added a new id generating if the same one was already used.
        – I generated the new id on the cloned x, not the original m.

        So now I get to load a module as many times I choose.
        Since my modules come from .php source I am using a serial numbering trick to make unique id’s for my modules. After that point I can use find with limited scope to get a fix on each individual control.

        I also added a login/logout functionality and made the menus reflect the user rights accordingly.
        What a nice framework …Thanks man.

        I will keep monitoring your progress on V3 , hopefully by that time I will have something worth donating back to you.
        cheers,

        petri.

      • Glad the framework does help your project. Just note that for the auto-generated IDs, make sure the server script can recognize them (when the layout is reloaded from local cookie or server store), and associate them with the right content.

        But things will be much simpler if you don’t want customizable layout, that just provide all users with one same layout.

  5. Hi,

    sorry about flooding your blog.
    Should I maybe post these questions somewhere else ?

    I’ve gotten quite comfortable with jpolitev2 after spending my christmas vacation coding and poking around (wife wasn’t too happy).

    I have progressed a lot with my applications. Basically the applications are a bunch of small jpolite windows (=modules) which know how to interact should they ever have the need to do so. Most of them do something with the database and I have a nice set of functions from previous life to facilitate that. Each of the separate modules are responsible for their own ui.

    This just led me to wonder that if I could republish these modules somehow in different portal environments. (not using iframes) .

    After all the windows/modules I build are very self sufficient, they need very little support from the system, except that they are given a div where they should render themselves using jquery functionality.
    Now jpolite supplies the divs to my modules, but looking from the viewpoint of one of my modules it really doesn’t care who donated the div.

    If someone (masterpage ?) loaded jquery and the needed plugins what would it take for modules to be properly loaded for e.g. inside a portlet ?

    Ideally I would like to approach each window/module as a “divlet” which could be rendered where ever I choose.

    Is this just a funny way to describe google gadgets ?
    What do you think ?
    Would this be feasible ?

    petri.

    • You may drop a note to my mailbox for further discussions, anyway, the comments should be helpful for others with similar questions.

      I think there’s no easy way to publish module/gadget/divlet without following specs/regulations from the masterpage/container, e.g., in JPolite, it is advised that no JS code in HTML.

      As I’ve tried in JPolite V1, Netvibes UWA makes use of iframe to load various modules from various providers. Since each module may come with its own specific libs (e.g., different versions of jQuery and/or prototype), there’s no way they can automatically stay peaceful in one document.

      To make lots modules work together in ONE page, only 2 choices seem viable:
      1.Use iframe, in this case, the modules can’t talk to each other easily. But it’s easy job for the masterpage, since all modules are self-contained.
      2.Use system defined module with custom parameters (e.g., RSS readers to different feeds). The modules can be DIVs that may interact with each other, which allow for more advanced functions, but the master page must do more to get the modules work right.

      In a real project, you may try a “hybrid” approach: DIVs for well-defined modules and Iframes for 3rd party ones.

  6. Got a question on Jpolite2

    The function of the adjust module layout.
    Is it supposed to be working?
    Is it needed with drag and drop layout?
    Just downloaded it the other day and it does not work.
    Is that because its not functional yet or are you deciding if its worth keeping?

    • Not sure what you mean by adjust module layout. If it’s about drag-n-drop of modules, then YES, it’s functional which uses jQuery UI libs behind the scene.

      If it doesn’t work, either try a different browser of check out the online version for possible errors.

  7. Hello again.
    I reworked what i have done somewhat on local server.
    I must have been tired or not paying attention.
    Shadowbox i got to work just like you said :-).
    Thx for that.
    next problem im having is incorporating like you did with shadowbox.
    mb.extruder specfically the left panel.
    Do you have any idea on how to do this?
    I have tried loading it like you did with the shadowbox.
    But im either missing something or mb.extruder cant be loaded at the same time as jpolite.
    Purpose is to get the left tab menu to be used along with shadowbox. It is a very good left panel for navigation.
    I could hard code it again but that would defeat the purpose of jpolite i guess.

    • MB.extruder will not be a feature of any given module, so better keep it out of JPolite scope.

      Note that adding more plugins/features may make usability suffer đŸ™‚ That is not to say JPolite is good for everything, just keep in mind that to “less is more” should be a guiding principle for building today’s website or software. At least for one, too many features will slow the download process.

  8. Im a bit confused but here is a explanation of my confusion.

    Im getting all things working which is nice as i delve into all the different aspects.
    But one thing that has confused me and i havent seen any posts on it is this.
    When JPolite loads up on local server or Live web server.
    Everything loads up just fine but the browser still shows that the pages are still be loaded.
    Fire Fox IE8 and safar all show the browser tab always active.
    Is this supposed to happen or is this a bug.
    Not likely i have broken anything.
    Source zip from google code the default demo does the same thing.

    • I haven’t come across similar problems, you can try loading with Firefox + Firebug to see if any error occur.

      BTW: the ZIP code is kinda old, better check out via SVN the latest version.

  9. Things are coming along nicely i think as far as gathering and stuff.
    Was wondering if you would drop by my test site and just take a look to see if i got things right so far.
    mbextruder seems to be loaded but im not so sure.
    dynatree seems to be load but again not so sure.
    shadowbox seems to be working nicely.
    Well if you have the time take a looksee.

  10. I wrote an issue for the bug log but don’t see any discussion there, so maybe I’ll try here.

    Has anyone else noticed drag and drop of modules is not as easy to use as jpolite1? Problems show up when dragging modules to an empty column for example.

    Just wondering why, and if anyone has attempted to fix it. I can see there are some intersection calculations going on, and someone told me he saw some 0 width elements that might be causing the problem.

    If fixes are expected or known, much appreciated to any pointers where to start. thx.

    • The drag & drop feature is done with jQuery UI, I’ve noticed the slowness and sometimes mis-behavior, but not quite sure why đŸ˜¦

      Might due to jQuery UI bugs or my wrong way of calling it. You may consult jQueryUI.com for more, now they’ve got 1.81a out.

  11. Hello. Working through some stuff here. Looking to add jquery dynatree. But im pretty much lost here. Cant find the call in the dynatree js file. But as i read your notes in the demo theres a way via fn.extend. How would i go about doing that? the normal call script is like this $(function(){ $(\"#tree\").dynatree({ // using default options }); }); Ive managed to add a few plugins via add control but they all had fn statements in the script. And they where called via a class or rel. dynatree is calling via a div id tag as you can see its #tree dynatree js /************************************************************************* jquery.dynatree.js Dynamic tree view control, with support for lazy loading of branches. Copyright (c) 2008-2009 Martin Wendt (http://wwWendt.de) Licensed under the MIT License (MIT-License.txt) A current version and some documentation is available at http://dynatree.googlecode.com/ Let me know, if you find bugs or improvements (martin at domain wwWendt.de). $Version: 0.5.2$ $Revision: 296, 2009-12-20 11:04:25$ @depends: jquery.js @depends: ui.core.js @depends: jquery.cookie.js *************************************************************************/ /************************************************************************* * Debug functions */ var _canLog = true; function _log(mode, msg) { /** * Usage: logMsg(\"%o was toggled\", this); */ if( !_canLog ) return; // Remove first argument var args = Array.prototype.slice.apply(arguments, [1]); // Prepend timestamp var dt = new Date(); var tag = dt.getHours()+\":\"+dt.getMinutes()+\":\"+dt.getSeconds()+\".\"+dt.getMilliseconds(); args[0] = tag + \" - \" + args[0]; try { switch( mode ) { case \"info\": window.console.info.apply(window.console, args); break; case \"warn\": window.console.warn.apply(window.console, args); break; default: window.console.log.apply(window.console, args); } } catch(e) { if( !window.console ) _canLog = false; // Permanently disable, when logging is not supported by the browser } } function logMsg(msg) { Array.prototype.unshift.apply(arguments, [\"debug\"]); _log.apply(this, arguments); } // Forward declaration var getDynaTreePersistData = undefined; /************************************************************************* * Constants */ var DTNodeStatus_Error = -1; var DTNodeStatus_Loading = 1; var DTNodeStatus_Ok = 0; // Start of local namespace ;(function($) { /************************************************************************* * Common tool functions. */ var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } } /************************************************************************* * Class DynaTreeNode */ var DynaTreeNode = Class.create(); DynaTreeNode.prototype = { initialize: function(parent, tree, data) { /** * @constructor */ this.parent = parent; this.tree = tree; if ( typeof data == \"string\" ) data = { title: data }; if( data.key == undefined ) data.key = \"_\" + tree._nodeCount++; this.data = $.extend({}, $.ui.dynatree.nodedatadefaults, data); this.div = null; // not yet created this.span = null; // not yet created this.childList = null; // no subnodes yet // this.isRead = false; // Lazy content not yet read this.isLoading = false; // Lazy content is being loaded this.hasSubSel = false; }, toString: function() { return \"dtnode: \'\" + this.data.title + \"\'\"; }, toDict: function(recursive, callback) { var dict = $.extend({}, this.data); dict.activate = ( this.tree.activeNode === this ); dict.focus = ( this.tree.focusNode === this ); dict.expand = this.bExpanded; dict.select = this.bSelected; if( callback ) callback(dict); if( recursive && this.childList ) { dict.children = []; for(var i=0; i0) || opts.minExpandLevel>1; var bHideFirstConnector = opts.rootVisible || opts.minExpandLevel>0; var res = \"\"; var p = this.parent; while( p ) { // Suppress first connector column, if visible top level is always expanded if ( bHideFirstConnector && p==rootParent ) break; res = ( p.isLastSibling() ? cache.tagEmpty : cache.tagVline) + res; p = p.parent; } // connector (expanded, expandable or simple) if( bHideFirstExpander && this.parent==rootParent ) { // skip connector } else if ( this.childList || this.data.isLazy ) { res += cache.tagExpander; } else { res += cache.tagConnector; } // Checkbox mode if( opts.checkbox && this.data.hideCheckbox!=true && !this.data.isStatusNode ) { res += cache.tagCheckbox; } // folder or doctype icon if ( this.data.icon ) { res += \"\"; } else if ( this.data.icon == false ) { // icon == false means \'no icon\' } else { // icon == null means \'default icon\' res += cache.tagNodeIcon; } // node name var tooltip = ( this.data && typeof this.data.tooltip == \"string\" ) ? \" title=\'\" + this.data.tooltip + \"\'\" : \"\"; res += \"\" + this.data.title + \"\"; return res; }, _fixOrder: function() { /** * Make sure, that order matches childList order. */ var cl = this.childList; if( !cl ) return; var childDiv = this.div.firstChild.nextSibling; for(var i=0; i<cl.length-1; i++) { var childNode1 = cl[i]; var childNode2 = childDiv.firstChild.dtnode; if( childNode1 !== childNode2 ) { // this.tree.logDebug("_fixOrder: mismatch at index " + i + ": " + childNode1 + " != " + childNode2); this.div.insertBefore(childNode1.div, childNode2.div); } else { childDiv = childDiv.nextSibling; } } }, render: function(bDeep, bHidden) { /** * Create HTML markup for this node. * * // This div contains the node\'s span and list of child div\'s. * S S S A // Span contains graphic spans and title tag * child1 * child2 * */ // this.tree.logDebug(\"%o.render()\", this); var opts = this.tree.options; var cn = opts.classNames; var isLastSib = this.isLastSibling(); // --- if( ! this.div ) { this.span = document.createElement(\"span\"); this.span.dtnode = this; if( this.data.key ) this.span.id = this.tree.options.idPrefix + this.data.key; this.div = document.createElement(\"div\"); this.div.appendChild(this.span); if ( this.parent ) { this.parent.div.appendChild(this.div); } if( this.parent==null && !this.tree.options.rootVisible ) this.span.style.display = \"none\"; } // set node connector images, links and text this.span.innerHTML = this._getInnerHtml(); // hide this node, if parent is collapsed this.div.style.display = ( this.parent==null || this.parent.bExpanded ? \"\" : \"none\"); // Set classes for current status var cnList = []; cnList.push( ( this.data.isFolder ) ? cn.folder : cn.document ); if( this.bExpanded ) cnList.push(cn.expanded); if( this.childList != null ) cnList.push(cn.hasChildren); if( this.data.isLazy && this.childList==null ) cnList.push(cn.lazy); if( isLastSib ) cnList.push(cn.lastsib); if( this.bSelected ) cnList.push(cn.selected); if( this.hasSubSel ) cnList.push(cn.partsel); if( this.tree.activeNode === this ) cnList.push(cn.active); if( this.data.addClass ) cnList.push(this.data.addClass); // IE6 doesn\'t correctly evaluate multiple class names, // so we create combined class names that can be used in the CSS cnList.push(cn.combinedExpanderPrefix + (this.bExpanded ? \"e\" : \"c\") + (this.data.isLazy && this.childList==null ? \"d\" : \"\") + (isLastSib ? \"l\" : \"\") ); cnList.push(cn.combinedIconPrefix + (this.bExpanded ? \"e\" : \"c\") + (this.data.isFolder ? \"f\" : \"\") ); this.span.className = cnList.join(\" \"); if( bDeep && this.childList && (bHidden || this.bExpanded) ) { for(var i=0; i<this.childList.length; i++) { this.childList[i].render(bDeep, bHidden) } this._fixOrder(); } }, hasChildren: function() { return this.childList != null; }, isLastSibling: function() { var p = this.parent; if ( !p ) return true; return p.childList[p.childList.length-1] === this; }, prevSibling: function() { if( !this.parent ) return null; var ac = this.parent.childList; for(var i=1; i<ac.length; i++) // start with 1, so prev(first) = null if( ac[i] === this ) return ac[i-1]; return null; }, nextSibling: function() { if( !this.parent ) return null; var ac = this.parent.childList; for(var i=0; i<ac.length-1; i++) // up to length-2, so next(last) = null if( ac[i] === this ) return ac[i+1]; return null; }, _setStatusNode: function(data) { // Create, modify or remove the status child node (pass 'null', to remove it). var firstChild = ( this.childList ? this.childList[0] : null ); if( !data ) { if ( firstChild ) { this.div.removeChild(firstChild.div); if( this.childList.length == 1 ) this.childList = null; else this.childList.shift(); } } else if ( firstChild ) { data.isStatusNode = true; firstChild.data = data; firstChild.render(false, false); } else { data.isStatusNode = true; firstChild = this.addChild(data); } }, setLazyNodeStatus: function(lts) { switch( lts ) { case DTNodeStatus_Ok: this._setStatusNode(null); // this.isRead = true; this.isLoading = false; this.render(false, false); if( this.tree.options.autoFocus ) { if( this === this.tree.tnRoot && !this.tree.options.rootVisible && this.childList ) { // special case: using ajaxInit this.childList[0].focus(); } else { this.focus(); } } break; case DTNodeStatus_Loading: this.isLoading = true; this._setStatusNode({ title: this.tree.options.strings.loading, addClass: this.tree.options.classNames.nodeWait }); break; case DTNodeStatus_Error: this.isLoading = false; this._setStatusNode({ title: this.tree.options.strings.loadError, addClass: this.tree.options.classNames.nodeError }); break; default: throw "Bad LazyNodeStatus: '" + lts + "'."; } }, _parentList: function(includeRoot, includeSelf) { var l = []; var dtn = includeSelf ? this : this.parent; while( dtn ) { if( includeRoot || dtn.parent ) l.unshift(dtn); dtn = dtn.parent; }; return l; }, getLevel: function() { var level = 0; var dtn = this.parent; while( dtn ) { level++; dtn = dtn.parent; }; return level; }, _getTypeForOuterNodeEvent: function(event) { /** Return the inner node span (title, checkbox or expander) if * event.target points to the outer span. * This function should fix issue #93: * FF2 ignores empty spans, when generating events (returning the parent instead). */ var cns = this.tree.options.classNames; var target = event.target; // Only process clicks on an outer node span (probably due to a FF2 event handling bug) if( target.className.indexOf(cns.folder)<0 && target.className.indexOf(cns.document)<0 ) { return null } // Event coordinates, relative to outer node span: var eventX = event.pageX - target.offsetLeft; var eventY = event.pageY - target.offsetTop; for(var i=0; i=x && eventX=y && eventY=0 || tcn.indexOf(cns.document)>=0 ) // FIX issue #93 return this._getTypeForOuterNodeEvent(event); return null; }, isVisible: function() { // Return true, if all parents are expanded. var parents = this._parentList(true, false); for(var i=0; i<parents.length; i++) if( ! parents[i].bExpanded ) return false; return true; }, makeVisible: function() { // Make sure, all parents are expanded var parents = this._parentList(true, false); for(var i=0; ia\").focus(); } catch(e) { } }, _activate: function(flag, fireEvents) { // (De)Activate - but not focus - this node. this.tree.logDebug(\"dtnode._activate(%o, fireEvents=%o) - %o\", flag, fireEvents, this); var opts = this.tree.options; if( this.data.isStatusNode ) return; if ( fireEvents && opts.onQueryActivate && opts.onQueryActivate.call(this.span, flag, this) == false ) return; // Callback returned false if( flag ) { // Activate if( this.tree.activeNode ) { if( this.tree.activeNode === this ) return; this.tree.activeNode.deactivate(); } if( opts.activeVisible ) this.makeVisible(); this.tree.activeNode = this; if( opts.persist ) $.cookie(opts.cookieId+\"-active\", this.data.key, opts.cookie); this.tree.persistence.activeKey = this.data.key; $(this.span).addClass(opts.classNames.active); if ( fireEvents && opts.onActivate ) // Pass element as \'this\' (jQuery convention) opts.onActivate.call(this.span, this); } else { // Deactivate if( this.tree.activeNode === this ) { var opts = this.tree.options; if ( opts.onQueryActivate && opts.onQueryActivate.call(this.span, false, this) == false ) return; // Callback returned false $(this.span).removeClass(opts.classNames.active); if( opts.persist ) { // Note: we don\'t pass null, but \'\'. So the cookie is not deleted. // If we pass null, we also have to pass a COPY of opts, because $cookie will override opts.expires (issue 84) $.cookie(opts.cookieId+\"-active\", \"\", opts.cookie); } this.tree.persistence.activeKey = null; this.tree.activeNode = null; if ( fireEvents && opts.onDeactivate ) opts.onDeactivate.call(this.span, this); } } }, activate: function() { // Select - but not focus - this node. // this.tree.logDebug(\"dtnode.activate(): %o\", this); this._activate(true, true); }, deactivate: function() { // this.tree.logDebug(\"dtnode.deactivate(): %o\", this); this._activate(false, true); }, isActive: function() { return (this.tree.activeNode === this); }, _userActivate: function() { // Handle user click / [space] / [enter], according to clickFolderMode. var activate = true; var expand = false; if ( this.data.isFolder ) { switch( this.tree.options.clickFolderMode ) { case 2: activate = false; expand = true; break; case 3: activate = expand = true; break; } } if( this.parent == null && this.tree.options.minExpandLevel>0 ) { expand = false; } if( expand ) { this.toggleExpand(); this.focus(); } if( activate ) { this.activate(); } }, _setSubSel: function(hasSubSel) { if( hasSubSel ) { this.hasSubSel = true; $(this.span).addClass(this.tree.options.classNames.partsel); } else { this.hasSubSel = false; $(this.span).removeClass(this.tree.options.classNames.partsel); } }, _fixSelectionState: function() { // fix selection status, for multi-hier mode // this.tree.logDebug(\"_fixSelectionState(%o) - %o\", this.bSelected, this); if( this.bSelected ) { // Select all children this.visit(function(dtnode){ dtnode.parent._setSubSel(true); dtnode._select(true, false, false); }); // Select parents, if all children are selected var p = this.parent; while( p ) { p._setSubSel(true); var allChildsSelected = true; for(var i=0; i<p.childList.length; i++) { var n = p.childList[i]; if( !n.bSelected && !n.data.isStatusNode ) { allChildsSelected = false; break; } } if( allChildsSelected ) p._select(true, false, false); p = p.parent; } } else { // Deselect all children this._setSubSel(false); this.visit(function(dtnode){ dtnode._setSubSel(false); dtnode._select(false, false, false); }); // Deselect parents, and recalc hasSubSel var p = this.parent; while( p ) { p._select(false, false, false); var isPartSel = false; for(var i=0; i<p.childList.length; i++) { if( p.childList[i].bSelected || p.childList[i].hasSubSel ) { isPartSel = true; break; } } p._setSubSel(isPartSel); p = p.parent; } } }, _select: function(sel, fireEvents, deep) { // Select - but not focus - this node. // this.tree.logDebug("dtnode._select(%o) - %o", sel, this); var opts = this.tree.options; if( this.data.isStatusNode ) return; // if( this.bSelected == sel ) { // this.tree.logDebug("dtnode._select(%o) IGNORED - %o", sel, this); return; } // Allow event listener to abort selection if ( fireEvents && opts.onQuerySelect && opts.onQuerySelect.call(this.span, sel, this) == false ) return; // Callback returned false // Force single-selection if( opts.selectMode==1 && sel ) { this.tree.visit(function(dtnode){ if( dtnode.bSelected ) { // Deselect; assuming that in selectMode:1 there's max. one other selected node dtnode._select(false, false, false); return false; } }); } this.bSelected = sel; // this.tree._changeNodeList("select", this, sel); if( sel ) { if( opts.persist ) this.tree.persistence.addSelect(this.data.key); $(this.span).addClass(opts.classNames.selected); if( deep && opts.selectMode==3 ) this._fixSelectionState(); if ( fireEvents && opts.onSelect ) opts.onSelect.call(this.span, true, this); } else { if( opts.persist ) this.tree.persistence.clearSelect(this.data.key); $(this.span).removeClass(opts.classNames.selected); if( deep && opts.selectMode==3 ) this._fixSelectionState(); if ( fireEvents && opts.onSelect ) opts.onSelect.call(this.span, false, this); } }, select: function(sel) { // Select - but not focus - this node. // this.tree.logDebug("dtnode.select(%o) - %o", sel, this); if( this.data.unselectable ) return this.bSelected; return this._select(sel!=false, true, true); }, toggleSelect: function() { // this.tree.logDebug("dtnode.toggleSelect() - %o", this); return this.select(!this.bSelected); }, isSelected: function() { return this.bSelected; }, _loadContent: function() { try { var opts = this.tree.options; this.tree.logDebug("_loadContent: start - %o", this); this.setLazyNodeStatus(DTNodeStatus_Loading); if( true == opts.onLazyRead.call(this.span, this) ) { // If function returns 'true', we assume that the loading is done: this.setLazyNodeStatus(DTNodeStatus_Ok); // Otherwise (i.e. if the loading was started as an asynchronous process) // the onLazyRead(dtnode) handler is expected to call dtnode.setLazyNodeStatus(DTNodeStatus_Ok/_Error) when done. this.tree.logDebug("_loadContent: succeeded - %o", this); } } catch(e) { alert(e); this.setLazyNodeStatus(DTNodeStatus_Error); } }, _expand: function(bExpand) { // this.tree.logDebug("dtnode._expand(%o) - %o", bExpand, this); if( this.bExpanded == bExpand ) { // this.tree.logDebug("dtnode._expand(%o) IGNORED - %o", bExpand, this); return; } var opts = this.tree.options; if( !bExpand && this.getLevel()<opts.minExpandLevel ) { this.tree.logDebug("dtnode._expand(%o) forced expand - %o", bExpand, this); return; } if ( opts.onQueryExpand && opts.onQueryExpand.call(this.span, bExpand, this) == false ) return; // Callback returned false this.bExpanded = bExpand; // Persist expand state if( opts.persist ) { if( bExpand ) this.tree.persistence.addExpand(this.data.key); else this.tree.persistence.clearExpand(this.data.key); } this.render(false); // Auto-collapse mode: collapse all siblings if( this.bExpanded && this.parent && opts.autoCollapse ) { var parents = this._parentList(false, true); for(var i=0; i<parents.length; i++) parents[i].collapseSiblings(); } // If the currently active node is now hidden, deactivate it if( opts.activeVisible && this.tree.activeNode && ! this.tree.activeNode.isVisible() ) { this.tree.activeNode.deactivate(); } // Expanding a lazy node: set 'loading...' and call callback if( bExpand && this.data.isLazy && this.childList==null && !this.isLoading ) { this._loadContent(); return; } // this.tree.logDebug("_expand: start div toggle - %o", this); var fxDuration = opts.fx ? (opts.fx.duration || 200) : 0; if( this.childList ) { for(var i=0; iDIV\" + (bExpand ? \":hidden\" : \":visible\"); if( opts.fx ) { var duration = opts.fx.duration || 200; // $(\">DIV\", this.div).animate(opts.fx, duration); $(filter, this.div).animate(opts.fx, duration); } else { $(filter, this.div).toggle(); // var $d = $(\">DIV\", this.div); // this.tree.logDebug(\"_expand: got div, start toggle - %o\", this); // $d.toggle(); } //*/ // this.tree.logDebug(\"_expand: end div toggle - %o\", this); if ( opts.onExpand ) opts.onExpand.call(this.span, bExpand, this); }, expand: function(flag) { if( !this.childList && !this.data.isLazy && flag ) return; // Prevent expanding empty nodes if( this.parent == null && this.tree.options.minExpandLevel>0 && !flag ) return; // Prevent collapsing the root this._expand(flag); }, toggleExpand: function() { this.expand(!this.bExpanded); }, collapseSiblings: function() { if( this.parent == null ) return; var ac = this.parent.childList; for (var i=0; i<ac.length; i++) { if ( ac[i] !== this && ac[i].bExpanded ) ac[i]._expand(false); } }, onClick: function(event) { // this.tree.logDebug("dtnode.onClick(" + event.type + "): dtnode:" + this + ", button:" + event.button + ", which: " + event.which); var targetType = this.getEventTargetType(event); if( targetType == "expander" ) { // Clicking the expander icon always expands/collapses this.toggleExpand(); this.focus(); // issue 95 } else if( targetType == "checkbox" ) { // Clicking the checkbox always (de)selects this.toggleSelect(); this.focus(); // issue 95 } else { this._userActivate(); // Chrome and Safari don't focus the a-tag on click this.span.getElementsByTagName("a")[0].focus(); } // Make sure that clicks stop, otherwise jumps to the top return false; }, onDblClick: function(event) { // this.tree.logDebug(\"dtnode.onDblClick(\" + event.type + \"): dtnode:\" + this + \", button:\" + event.button + \", which: \" + event.which); }, onKeydown: function(event) { // this.tree.logDebug(\"dtnode.onKeydown(\" + event.type + \"): dtnode:\" + this + \", charCode:\" + event.charCode + \", keyCode: \" + event.keyCode + \", which: \" + event.which); var handled = true; // alert(\"keyDown\" + event.which); switch( event.which ) { // charCodes: // case 43: // \'+\' case 107: // \'+\' case 187: // \'+\' @ Chrome, Safari if( !this.bExpanded ) this.toggleExpand(); break; // case 45: // \'-\' case 109: // \'-\' case 189: // \'+\' @ Chrome, Safari if( this.bExpanded ) this.toggleExpand(); break; //~ case 42: // \'*\' //~ break; //~ case 47: // \'/\' //~ break; // case 13: // // on a focused tag seems to generate a click-event. // this._userActivate(); // break; case 32: // this._userActivate(); break; case 8: // if( this.parent ) this.parent.focus(); break; case 37: // if( this.bExpanded ) { this.toggleExpand(); this.focus(); } else if( this.parent && (this.tree.options.rootVisible || this.parent.parent) ) { this.parent.focus(); } break; case 39: // if( !this.bExpanded && (this.childList || this.data.isLazy) ) { this.toggleExpand(); this.focus(); } else if( this.childList ) { this.childList[0].focus(); } break; case 38: // var sib = this.prevSibling(); while( sib && sib.bExpanded && sib.childList ) sib = sib.childList[sib.childList.length-1]; if( !sib && this.parent && (this.tree.options.rootVisible || this.parent.parent) ) sib = this.parent; if( sib ) sib.focus(); break; case 40: // var sib; if( this.bExpanded && this.childList ) { sib = this.childList[0]; } else { var parents = this._parentList(false, true); for(var i=parents.length-1; i>=0; i--) { sib = parents[i].nextSibling(); if( sib ) break; } } if( sib ) sib.focus(); break; default: handled = false; } // Return false, if handled, to prevent default processing return !handled; }, onKeypress: function(event) { // onKeypress is only hooked to allow user callbacks. // We don\'t process it, because IE and Safari don\'t fire keypress for cursor keys. // this.tree.logDebug(\"dtnode.onKeypress(\" + event.type + \"): dtnode:\" + this + \", charCode:\" + event.charCode + \", keyCode: \" + event.keyCode + \", which: \" + event.which); }, onFocus: function(event) { // Handles blur and focus events. // this.tree.logDebug(\"dtnode.onFocus(%o): %o\", event, this); var opts = this.tree.options; if ( event.type==\"blur\" || event.type==\"focusout\" ) { if ( opts.onBlur ) // Pass element as \'this\' (jQuery convention) opts.onBlur.call(this.span, this); if( this.tree.tnFocused ) $(this.tree.tnFocused.span).removeClass(opts.classNames.focused); this.tree.tnFocused = null; if( opts.persist ) $.cookie(opts.cookieId+\"-focus\", \"\", opts.cookie); } else if ( event.type==\"focus\" || event.type==\"focusin\") { // Fix: sometimes the blur event is not generated if( this.tree.tnFocused && this.tree.tnFocused !== this ) { this.tree.logDebug(\"dtnode.onFocus: out of sync: curFocus: %o\", this.tree.tnFocused); $(this.tree.tnFocused.span).removeClass(opts.classNames.focused); } this.tree.tnFocused = this; if ( opts.onFocus ) // Pass element as \'this\' (jQuery convention) opts.onFocus.call(this.span, this); $(this.tree.tnFocused.span).addClass(opts.classNames.focused); if( opts.persist ) $.cookie(opts.cookieId+\"-focus\", this.data.key, opts.cookie); } // TODO: return anything? // return false; }, visit: function(fn, data, includeSelf) { // Call fn(dtnode, data) for all child nodes. Stop iteration, if fn() returns false. var n = 0; if( includeSelf == true ) { if( fn(this, data) == false ) return 1; n++; } if ( this.childList ) for (var i=0; i<this.childList.length; i++) n += this.childList[i].visit(fn, data, true); return n; }, remove: function() { // Remove this node // this.tree.logDebug ("%o.remove()", this); if ( this === this.tree.root ) return false; return this.parent.removeChild(this); }, removeChild: function(tn) { // Remove tn from list of direct children. var ac = this.childList; if( ac.length == 1 ) { if( tn !== ac[0] ) throw "removeChild: invalid child"; return this.removeChildren(); } if( tn === this.tree.activeNode ) tn.deactivate(); if( this.tree.options.persist ) { if( tn.bSelected ) this.tree.persistence.clearSelect(tn.data.key); if ( tn.bExpanded ) this.tree.persistence.clearExpand(tn.data.key); } tn.removeChildren(true); this.div.removeChild(tn.div); for(var i=0; i<ac.length; i++) { if( ac[i] === tn ) { this.childList.splice(i, 1); delete tn; break; } } }, removeChildren: function(isRecursiveCall, retainPersistence) { // Remove all child nodes (more efficiently than recursive remove()) // this.tree.logDebug ("%o.removeChildren(%o)", this, isRecursiveCall); var tree = this.tree; var ac = this.childList; if( ac ) { for(var i=0; i<ac.length; i++) { var tn=ac[i]; // this.tree.logDebug ("del %o", tn); if ( tn === tree.activeNode && !retainPersistence ) tn.deactivate(); if( this.tree.options.persist && !retainPersistence ) { if( tn.bSelected ) this.tree.persistence.clearSelect(tn.data.key); if ( tn.bExpanded ) this.tree.persistence.clearExpand(tn.data.key); } tn.removeChildren(true, retainPersistence); this.div.removeChild(tn.div); delete tn; } this.childList = null; } if( ! isRecursiveCall ) { // this._expand(false); // this.isRead = false; this.isLoading = false; this.render(false, false); } }, reload: function(force) { // Discard lazy content (and reload, if node was expanded). if( this.parent == null ) return this.tree.reload(); if( ! this.data.isLazy ) throw "node.reload() requires lazy nodes."; if( this.bExpanded ) { this.expand(false); this.removeChildren(); this.expand(true); } else { this.removeChildren(); if( force ) this._loadContent(); } }, _addChildNode: function(dtnode, beforeNode) { /** * Internal function to add one single DynatreeNode as a child. * */ var tree = this.tree; var opts = tree.options; var pers = tree.persistence; // tree.logDebug("%o._addChildNode(%o)", this, dtnode); // --- Update and fix dtnode attributes if necessary dtnode.parent = this; // if( beforeNode && (beforeNode.parent !== this || beforeNode === dtnode ) ) // throw " must be another child of \"; // --- Add dtnode as a child if ( this.childList==null ) { this.childList = []; } else if( ! beforeNode ) { // Fix \'lastsib\' $(this.childList[this.childList.length-1].span).removeClass(opts.classNames.lastsib); } if( beforeNode ) { var iBefore = $.inArray(beforeNode, this.childList); if( iBefore < 0 ) throw " must be a child of \"; this.childList.splice(iBefore, 0, dtnode); // alert(this.childList); } else { // Append node this.childList.push(dtnode); } // --- Handle persistence // Initial status is read from cookies, if persistence is active and // cookies are already present. // Otherwise the status is read from the data attributes and then persisted. var isInitializing = tree.isInitializing(); if( opts.persist && pers.cookiesFound && isInitializing ) { // Init status from cookies // tree.logDebug(\"init from cookie, pa=%o, dk=%o\", pers.activeKey, dtnode.data.key); if( pers.activeKey == dtnode.data.key ) tree.activeNode = dtnode; if( pers.focusedKey == dtnode.data.key ) tree.focusNode = dtnode; dtnode.bExpanded = ($.inArray(dtnode.data.key, pers.expandedKeyList) >= 0); dtnode.bSelected = ($.inArray(dtnode.data.key, pers.selectedKeyList) >= 0); // tree.logDebug(\" key=%o, bSelected=%o\", dtnode.data.key, dtnode.bSelected); } else { // Init status from data (Note: we write the cookies after the init phase) // tree.logDebug(\"init from data\"); if( dtnode.data.activate ) { tree.activeNode = dtnode; if( opts.persist ) pers.activeKey = dtnode.data.key; } if( dtnode.data.focus ) { tree.focusNode = dtnode; if( opts.persist ) pers.focusedKey = dtnode.data.key; } dtnode.bExpanded = ( dtnode.data.expand == true ); // Collapsed by default if( dtnode.bExpanded && opts.persist ) pers.addExpand(dtnode.data.key); dtnode.bSelected = ( dtnode.data.select == true ); // Deselected by default /* Doesn\'t work, cause pers.selectedKeyList may be null if( dtnode.bSelected && opts.selectMode==1 && pers.selectedKeyList && pers.selectedKeyList.length>0 ) { tree.logWarning(\"Ignored multi-selection in single-mode for %o\", dtnode); dtnode.bSelected = false; // Fixing bad input data (multi selection for mode:1) } */ if( dtnode.bSelected && opts.persist ) pers.addSelect(dtnode.data.key); } // Always expand, if it\'s below minExpandLevel // tree.logDebug (\"%o._addChildNode(%o), l=%o\", this, dtnode, dtnode.getLevel()); if ( opts.minExpandLevel >= dtnode.getLevel() ) { // tree.logDebug (\"Force expand for %o\", dtnode); this.bExpanded = true; } // In multi-hier mode, update the parents selection state // issue #82: only if not initializing, because the children may not exist yet // if( !dtnode.data.isStatusNode && opts.selectMode==3 && !isInitializing ) // dtnode._fixSelectionState(); // In multi-hier mode, update the parents selection state if( dtnode.bSelected && opts.selectMode==3 ) { var p = this; while( p ) { if( !p.hasSubSel ) p._setSubSel(true); p = p.parent; } } // render this node and the new child if ( tree.bEnableUpdate ) this.render(true, true); return dtnode; }, addChild: function(obj, beforeNode) { /** * Add a node object as child. * * This should be the only place, where a DynaTreeNode is constructed! * (Except for the root node creation in the tree constructor) * * @param obj A JS object (may be recursive) or an array of those. * @param {DynaTreeNode} beforeNode (optional) sibling node. * * Data format: array of node objects, with optional \'children\' attributes. * [ * { title: \"t1\", isFolder: true, ... } * { title: \"t2\", isFolder: true, ..., * children: [ * {title: \"t2.1\", ..}, * {..} * ] * } * ] * A simple object is also accepted instead of an array. * */ // this.tree.logDebug(\"%o.addChild(%o, %o)\", this, obj, beforeNode); if( !obj || obj.length==0 ) // Passed null or undefined or empty array return; if( obj instanceof DynaTreeNode ) return this._addChildNode(obj, beforeNode); if( !obj.length ) // Passed a single data object obj = [ obj ]; var prevFlag = this.tree.enableUpdate(false); var tnFirst = null; for (var i=0; i<obj.length; i++) { var data = obj[i]; var dtnode = this._addChildNode(new DynaTreeNode(this, this.tree, data), beforeNode); if( !tnFirst ) tnFirst = dtnode; // Add child nodes recursively if( data.children ) dtnode.addChild(data.children, null); } this.tree.enableUpdate(prevFlag); return tnFirst; }, append: function(obj) { this.tree.logWarning("node.append() is deprecated (use node.addChild() instead)."); return this.addChild(obj, null); }, appendAjax: function(ajaxOptions) { this.removeChildren(false, true); this.setLazyNodeStatus(DTNodeStatus_Loading); // Ajax option inheritance: $.ajaxSetup < $.ui.dynatree.defaults.ajaxDefaults < tree.options.ajaxDefaults < ajaxOptions var self = this; var orgSuccess = ajaxOptions.success; var orgError = ajaxOptions.error; var options = $.extend({}, this.tree.options.ajaxDefaults, ajaxOptions, { /* complete: function(req, textStatus){ alert("ajax complete"); }, timeout: 5000, // 5 sec */ success: function(data, textStatus){ // is the request options // self.tree.logDebug(\"appendAjax().success\"); var prevPhase = self.tree.phase; self.tree.phase = \"init\"; // self.append(data); self.addChild(data, null); self.tree.phase = \"postInit\"; self.setLazyNodeStatus(DTNodeStatus_Ok); if( orgSuccess ) orgSuccess.call(options, self); self.tree.phase = prevPhase; }, error: function(XMLHttpRequest, textStatus, errorThrown){ // is the request options // self.tree.logDebug(\"appendAjax().error\"); self.setLazyNodeStatus(DTNodeStatus_Error); if( orgError ) orgError.call(options, self, XMLHttpRequest, textStatus, errorThrown); } }); $.ajax(options); }, // --- end of class lastentry: undefined } /************************************************************************* * class DynaTreeStatus */ var DynaTreeStatus = Class.create(); DynaTreeStatus._getTreePersistData = function(cookieId, cookieOpts) { // Static member: Return persistence information from cookies var ts = new DynaTreeStatus(cookieId, cookieOpts); ts.read(); return ts.toDict(); } // Make available in global scope getDynaTreePersistData = DynaTreeStatus._getTreePersistData; DynaTreeStatus.prototype = { // Constructor initialize: function(cookieId, cookieOpts) { this._log(\"DynaTreeStatus: initialize\"); if( cookieId === undefined ) cookieId = $.ui.dynatree.defaults.cookieId; cookieOpts = $.extend({}, $.ui.dynatree.defaults.cookie, cookieOpts); this.cookieId = cookieId; this.cookieOpts = cookieOpts; this.cookiesFound = undefined; this.activeKey = null; this.focusedKey = null; this.expandedKeyList = null; this.selectedKeyList = null; }, // member functions _log: function(msg) { // this.logDebug(\"_changeNodeList(%o): nodeList:%o, idx:%o\", mode, nodeList, idx); Array.prototype.unshift.apply(arguments, [\"debug\"]); _log.apply(this, arguments); }, read: function() { this._log(\"DynaTreeStatus: read\"); // Read or init cookies. this.cookiesFound = false; var cookie = $.cookie(this.cookieId + \"-active\"); this.activeKey = ( cookie == null ) ? \"\" : cookie; if( cookie != null ) this.cookiesFound = true; cookie = $.cookie(this.cookieId + \"-focus\"); this.focusedKey = ( cookie == null ) ? \"\" : cookie; if( cookie != null ) this.cookiesFound = true; cookie = $.cookie(this.cookieId + \"-expand\"); this.expandedKeyList = ( cookie == null ) ? [] : cookie.split(\",\"); if( cookie != null ) this.cookiesFound = true; cookie = $.cookie(this.cookieId + \"-select\"); this.selectedKeyList = ( cookie == null ) ? [] : cookie.split(\",\"); if( cookie != null ) this.cookiesFound = true; }, write: function() { this._log(\"DynaTreeStatus: write\"); $.cookie(this.cookieId + \"-active\", ( this.activeKey == null ) ? \"\" : this.activeKey, this.cookieOpts); $.cookie(this.cookieId + \"-focus\", ( this.focusedKey == null ) ? \"\" : this.focusedKey, this.cookieOpts); $.cookie(this.cookieId + \"-expand\", ( this.expandedKeyList == null ) ? \"\" : this.expandedKeyList.join(\",\"), this.cookieOpts); $.cookie(this.cookieId + \"-select\", ( this.selectedKeyList == null ) ? \"\" : this.selectedKeyList.join(\",\"), this.cookieOpts); }, addExpand: function(key) { this._log(\"addExpand(%o)\", key); if( $.inArray(key, this.expandedKeyList) = 0 ) { this.expandedKeyList.splice(idx, 1); $.cookie(this.cookieId + \"-expand\", this.expandedKeyList.join(\",\"), this.cookieOpts); } }, addSelect: function(key) { this._log(\"addSelect(%o)\", key); if( $.inArray(key, this.selectedKeyList) = 0 ) { this.selectedKeyList.splice(idx, 1); $.cookie(this.cookieId + \"-select\", this.selectedKeyList.join(\",\"), this.cookieOpts); } }, isReloading: function() { return this.cookiesFound == true; }, toDict: function() { return { cookiesFound: this.cookiesFound, activeKey: this.activeKey, focusedKey: this.activeKey, expandedKeyList: this.expandedKeyList, selectedKeyList: this.selectedKeyList }; }, // --- end of class lastentry: undefined }; /************************************************************************* * class DynaTree */ var DynaTree = Class.create(); // --- Static members ---------------------------------------------------------- DynaTree.version = \"$Version: 0.5.2$\"; /* DynaTree._initTree = function() { }; DynaTree._bind = function() { }; */ //--- Class members ------------------------------------------------------------ DynaTree.prototype = { // Constructor // initialize: function(divContainer, options) { initialize: function($widget) { // instance members this.phase = \"init\"; this.$widget = $widget; this.options = $widget.options; this.$tree = $widget.element; // find container element this.divTree = this.$tree.get(0); }, // member functions _load: function() { var $widget = this.$widget; var opts = this.options; this.bEnableUpdate = true; this._nodeCount = 1; this.activeNode = null; this.focusNode = null; // If a \'options.classNames\' dictionary was passed, still use defaults // for undefined classes: if( opts.classNames !== $.ui.dynatree.defaults.classNames ) { opts.classNames = $.extend({}, $.ui.dynatree.defaults.classNames, opts.classNames); } // Guess skin path, if not specified if(!opts.imagePath) { $(\"script\").each( function () { // Eclipse syntax parser breaks on this expression, so put it at the bottom: if( this.src.search(_rexDtLibName) >= 0 ) { if( this.src.indexOf(\"/\")>=0 ) // issue #47 opts.imagePath = this.src.slice(0, this.src.lastIndexOf(\"/\")) + \"/skin/\"; else opts.imagePath = \"skin/\"; // logMsg(\"Guessing imagePath from \'%s\': \'%s\'\", this.src, opts.imagePath); return false; // first match } }); } this.persistence = new DynaTreeStatus(opts.cookieId, opts.cookie); if( opts.persist ) { if( !$.cookie ) _log(\"warn\", \"Please include jquery.cookie.js to use persistence.\"); this.persistence.read(); } this.logDebug(\"DynaTree.persistence: %o\", this.persistence.toDict()); // Cached tag strings this.cache = { tagEmpty: \"\", tagVline: \"\", tagExpander: \"\", tagConnector: \"\", tagNodeIcon: \"\", tagCheckbox: \"\", lastentry: undefined }; // Clear container, in case it contained some \'waiting\' or \'error\' text // for clients that don\'t support JS. // We don\'t do this however, if we try to load from an embedded UL element. if( opts.children || (opts.initAjax && opts.initAjax.url) || opts.initId ) $(this.divTree).empty(); else if( this.divRoot ) $(this.divRoot).remove(); // create the root element this.tnRoot = new DynaTreeNode(null, this, {title: opts.title, key: \"root\"}); this.tnRoot.data.isFolder = true; this.tnRoot.render(false, false); this.divRoot = this.tnRoot.div; this.divRoot.className = opts.classNames.container; // add root to container // TODO: this should be delayed until all children have been created for performance reasons this.divTree.appendChild(this.divRoot); var root = this.tnRoot; var isReloading = ( opts.persist && this.persistence.isReloading() ); var isLazy = false; var prevFlag = this.enableUpdate(false); this.logDebug(\"Dynatree._load(): read tree structure...\"); // Init tree structure if( opts.children ) { // Read structure from node array root.addChild(opts.children); } else if( opts.initAjax && opts.initAjax.url ) { // Init tree from AJAX request isLazy = true; root.data.isLazy = true; this._reloadAjax(); } else if( opts.initId ) { // Init tree from another UL element this._createFromTag(root, $(\"#\"+opts.initId)); } else { // Init tree from the first UL element inside the container var $ul = this.$tree.find(\">ul\").hide(); this._createFromTag(root, $ul); $ul.remove(); } this._checkConsistency(); // Render html markup this.logDebug(\"Dynatree._load(): render nodes...\"); this.enableUpdate(prevFlag); // bind event handlers this.logDebug(\"Dynatree._load(): bind events...\"); this.$widget.bind(); // --- Post-load processing this.logDebug(\"Dynatree._load(): postInit...\"); this.phase = \"postInit\"; // In persist mode, make sure that cookies are written, even if they are empty if( opts.persist ) { this.persistence.write(); } // Set focus, if possible (this will also fire an event and write a cookie) if( this.focusNode && this.focusNode.isVisible() ) { this.logDebug(\"Focus on init: %o\", this.focusNode); this.focusNode.focus(); } if( !isLazy && opts.onPostInit ) { opts.onPostInit.call(this, isReloading, false); } this.phase = \"idle\"; }, _reloadAjax: function() { // Reload var opts = this.options; if( ! opts.initAjax || ! opts.initAjax.url ) throw \"tree.reload() requires \'initAjax\' mode.\"; var pers = this.persistence; var ajaxOpts = $.extend({}, opts.initAjax); // Append cookie info to the request // this.logDebug(\"reloadAjax: key=%o, an.key:%o\", pers.activeKey, this.activeNode?this.activeNode.data.key:\"?\"); if( ajaxOpts.addActiveKey ) ajaxOpts.data.activeKey = pers.activeKey; if( ajaxOpts.addFocusedKey ) ajaxOpts.data.focusedKey = pers.focusedKey; if( ajaxOpts.addExpandedKeyList ) ajaxOpts.data.expandedKeyList = pers.expandedKeyList.join(\",\"); if( ajaxOpts.addSelectedKeyList ) ajaxOpts.data.selectedKeyList = pers.selectedKeyList.join(\",\"); // Set up onPostInit callback to be called when Ajax returns if( opts.onPostInit ) { if( ajaxOpts.success ) this.tree.logWarning(\"initAjax: success callback is ignored when onPostInit was specified.\"); if( ajaxOpts.error ) this.tree.logWarning(\"initAjax: error callback is ignored when onPostInit was specified.\"); var isReloading = pers.isReloading(); ajaxOpts[\"success\"] = function(dtnode) { opts.onPostInit.call(dtnode.tree, isReloading, false); }; ajaxOpts[\"error\"] = function(dtnode) { opts.onPostInit.call(dtnode.tree, isReloading, true); }; } this.logDebug(\"Dynatree._init(): send Ajax request...\"); this.tnRoot.appendAjax(ajaxOpts); }, toString: function() { return \"DynaTree \'\" + this.options.title + \"\'\"; }, toDict: function() { return this.tnRoot.toDict(true); }, getPersistData: function() { return this.persistence.toDict(); }, logDebug: function(msg) { if( this.options.debugLevel >= 2 ) { Array.prototype.unshift.apply(arguments, [\"debug\"]); _log.apply(this, arguments); } }, logInfo: function(msg) { if( this.options.debugLevel >= 1 ) { Array.prototype.unshift.apply(arguments, [\"info\"]); _log.apply(this, arguments); } }, logWarning: function(msg) { Array.prototype.unshift.apply(arguments, [\"warn\"]); _log.apply(this, arguments); }, isInitializing: function() { return ( this.phase==\"init\" || this.phase==\"postInit\" ); }, isReloading: function() { return ( this.phase==\"init\" || this.phase==\"postInit\" ) && this.options.persist && this.persistence.cookiesFound; }, isUserEvent: function() { return ( this.phase==\"userEvent\" ); }, redraw: function() { this.logDebug(\"dynatree.redraw()...\"); this.tnRoot.render(true, true); this.logDebug(\"dynatree.redraw() done.\"); }, reloadAjax: function() { this.logWarning(\"tree.reloadAjax() is deprecated since v0.5.2 (use reload() instead).\"); }, reload: function() { this._load(); }, getRoot: function() { return this.tnRoot; }, getNodeByKey: function(key) { // $(\"#...\") has problems, if the key contains \'.\', so we use getElementById() // return $(\"#\" + this.options.idPrefix + key).attr(\"dtnode\"); var el = document.getElementById(this.options.idPrefix + key); return ( el && el.dtnode ) ? el.dtnode : null; }, getActiveNode: function() { return this.activeNode; }, reactivate: function(setFocus) { // Re-fire onQueryActivate and onActivate events. var node = this.activeNode; // this.logDebug(\"reactivate %o\", node); if( node ) { this.activeNode = null; // Force re-activating node.activate(); if( setFocus ) node.focus(); } }, getSelectedNodes: function(stopOnParents) { var nodeList = []; this.tnRoot.visit(function(dtnode){ if( dtnode.bSelected ) { nodeList.push(dtnode); if( stopOnParents == true ) return false; // stop processing this branch } }); return nodeList; }, activateKey: function(key) { var dtnode = (key === null) ? null : this.getNodeByKey(key); if( !dtnode ) { if( this.activeNode ) this.activeNode.deactivate(); this.activeNode = null; return null; } dtnode.focus(); dtnode.activate(); return dtnode; }, selectKey: function(key, select) { var dtnode = this.getNodeByKey(key); if( !dtnode ) return null; dtnode.select(select); return dtnode; }, enableUpdate: function(bEnable) { if ( this.bEnableUpdate==bEnable ) return bEnable; this.bEnableUpdate = bEnable; if ( bEnable ) this.redraw(); return !bEnable; // return previous value }, visit: function(fn, data, includeRoot) { return this.tnRoot.visit(fn, data, includeRoot); }, _createFromTag: function(parentTreeNode, $ulParent) { // Convert a ... list into children of the parent tree node. var self = this; /* TODO: better? this.$lis = $(\"li:has(a[href])\", this.element); this.$tabs = this.$lis.map(function() { return $(\"a\", this)[0]; }); */ $ulParent.find(\">li\").each(function() { var $li = $(this); var $liSpan = $li.find(\">span:first\"); var title; if( $liSpan.length ) { // If a tag is specified, use it literally. title = $liSpan.html(); } else { // If only a tag is specified, use the trimmed string up to the next child tag. title = $li.html(); var iPos = title.search(/=0 ) title = $.trim(title.substring(0, iPos)); else title = $.trim(title); // self.logDebug(\"%o\", title); } // Parse node options from ID, title and class attributes var data = { title: title, isFolder: $li.hasClass(\"folder\"), isLazy: $li.hasClass(\"lazy\"), expand: $li.hasClass(\"expanded\"), select: $li.hasClass(\"selected\"), activate: $li.hasClass(\"active\"), focus: $li.hasClass(\"focused\") }; if( $li.attr(\"title\") ) data.tooltip = $li.attr(\"title\"); if( $li.attr(\"id\") ) data.key = $li.attr(\"id\"); // If a data attribute is present, evaluate as a JavaScript object if( $li.attr(\"data\") ) { var dataAttr = $.trim($li.attr(\"data\")); if( dataAttr ) { if( dataAttr.charAt(0) != \"{\" ) dataAttr = \"{\" + dataAttr + \"}\" try { $.extend(data, eval(\"(\" + dataAttr + \")\")); } catch(e) { throw (\"Error parsing node data: \" + e + \"ndata:n\'\" + dataAttr + \"\'\"); } } } childNode = parentTreeNode.addChild(data); // Recursive reading of child nodes, if LI tag contains an UL tag var $ul = $li.find(\">ul:first\"); if( $ul.length ) { self._createFromTag(childNode, $ul); // must use \'self\', because \'this\' is the each() context } }); }, _checkConsistency: function() { // this.logDebug(\"tree._checkConsistency() NOT IMPLEMENTED - %o\", this); }, // --- end of class lastentry: undefined }; /************************************************************************* * widget $(..).dynatree */ $.widget(\"ui.dynatree\", { init: function() { // ui.core 1.6 renamed init() to _init(): this stub assures backward compatibility _log(\"warn\", \"ui.dynatree.init() was called; you should upgrade to ui.core.js v1.6 or higher.\"); return this._init(); }, _init: function() { logMsg(\"Dynatree._init(): version=\'%s\', debugLevel=%o.\", DynaTree.version, this.options.debugLevel); var opts = this.options; // The widget framework supplies this.element and this.options. this.options.event += \".dynatree\"; // namespace event var divTree = this.element.get(0); /* // Clear container, in case it contained some \'waiting\' or \'error\' text // for clients that don\'t support JS if( opts.children || (opts.initAjax && opts.initAjax.url) || opts.initId ) $(divTree).empty(); */ // Create the DynaTree object this.tree = new DynaTree(this); this.tree._load(); this.tree.logDebug(\"Dynatree._init(): done.\"); }, bind: function() { var $this = this.element; var o = this.options; // Prevent duplicate binding this.unbind(); // Tool function to get dtnode from the event target: function __getNodeFromElement(el) { var iMax = 5; while( el && iMax-- ) { if( el.dtnode ) return el.dtnode; el = el.parentNode; }; return null; } var eventNames = \"click.dynatree dblclick.dynatree\"; if( o.keyboard ) // Note: leading \' \'! eventNames += \" keypress.dynatree keydown.dynatree\"; $this.bind(eventNames, function(event){ var dtnode = __getNodeFromElement(event.target); if( !dtnode ) return true; // Allow bubbling of other events var prevPhase = dtnode.tree.phase; dtnode.tree.phase = \"userEvent\"; try { dtnode.tree.logDebug(\"bind(%o): dtnode: %o\", event, dtnode); switch(event.type) { case \"click\": return ( o.onClick && o.onClick(dtnode, event)===false ) ? false : dtnode.onClick(event); case \"dblclick\": return ( o.onDblClick && o.onDblClick(dtnode, event)===false ) ? false : dtnode.onDblClick(event); case \"keydown\": return ( o.onKeydown && o.onKeydown(dtnode, event)===false ) ? false : dtnode.onKeydown(event); case \"keypress\": return ( o.onKeypress && o.onKeypress(dtnode, event)===false ) ? false : dtnode.onKeypress(event); }; } catch(e) { var _ = null; // issue 117 // dtnode.tree.logError(\"bind(%o): dtnode: %o\", event, dtnode); } finally { dtnode.tree.phase = prevPhase; } }); // focus/blur don\'t bubble, i.e. are not delegated to parent tags, // so we use the addEventListener capturing phase. // See http://www.howtocreate.co.uk/tutorials/javascript/domevents function __focusHandler(event) { // Handles blur and focus. // Fix event for IE: event = arguments[0] = $.event.fix( event || window.event ); var dtnode = __getNodeFromElement(event.target); return dtnode ? dtnode.onFocus(event) : false; } var div = this.tree.divTree; if( div.addEventListener ) { div.addEventListener(\"focus\", __focusHandler, true); div.addEventListener(\"blur\", __focusHandler, true); } else { div.onfocusin = div.onfocusout = __focusHandler; } // EVENTS // disable click if event is configured to something else // if (!(/^click/).test(o.event)) // this.$tabs.bind(\"click.tabs\", function() { return false; }); }, unbind: function() { this.element.unbind(\".dynatree\"); }, /* TODO: we could handle option changes during runtime here (maybe to re-render, ...) setData: function(key, value) { this.tree.logDebug(\"dynatree.setData(\'\" + key + \"\', \'\" + value + \"\')\"); }, */ enable: function() { this.bind(); // Call default disable(): remove -disabled from css: $.widget.prototype.enable.apply(this, arguments); }, disable: function() { this.unbind(); // Call default disable(): add -disabled to css: $.widget.prototype.disable.apply(this, arguments); }, // --- getter methods (i.e. NOT returning a reference to $) getTree: function() { return this.tree; }, getRoot: function() { return this.tree.getRoot(); }, getActiveNode: function() { return this.tree.getActiveNode(); }, getSelectedNodes: function() { return this.tree.getSelectedNodes(); }, // ------------------------------------------------------------------------ lastentry: undefined }); // The following methods return a value (thus breaking the jQuery call chain): $.ui.dynatree.getter = \"getTree getRoot getActiveNode getSelectedNodes\"; // Plugin default options: $.ui.dynatree.defaults = { title: \"Dynatree root\", // Name of the root node. rootVisible: false, // Set to true, to make the root node visible. minExpandLevel: 1, // 1: root node is not collapsible imagePath: null, // Path to a folder containing icons. Defaults to \'skin/\' subdirectory. children: null, // Init tree structure from this object array. initId: null, // Init tree structure from a element with this ID. initAjax: null, // Ajax options used to initialize the tree strucuture. autoFocus: true, // Set focus to first child, when expanding or lazy-loading. keyboard: true, // Support keyboard navigation. persist: false, // Persist expand-status to a cookie autoCollapse: false, // Automatically collapse all siblings, when a node is expanded. clickFolderMode: 3, // 1:activate, 2:expand, 3:activate and expand activeVisible: true, // Make sure, active nodes are visible (expanded). checkbox: false, // Show checkboxes. selectMode: 2, // 1:single, 2:multi, 3:multi-hier fx: null, // Animations, e.g. null or { height: \"toggle\", duration: 200 } // Low level event handlers: onEvent(dtnode, event): return false, to stop default processing onClick: null, // null: generate focus, expand, activate, select events. onDblClick: null, // (No default actions.) onKeydown: null, // null: generate keyboard navigation (focus, expand, activate). onKeypress: null, // (No default actions.) onFocus: null, // null: set focus to node. onBlur: null, // null: remove focus from node. // Pre-event handlers onQueryEvent(flag, dtnode): return false, to stop processing onQueryActivate: null, // Callback(flag, dtnode) before a node is (de)activated. onQuerySelect: null, // Callback(flag, dtnode) before a node is (de)selected. onQueryExpand: null, // Callback(flag, dtnode) before a node is expanded/collpsed. // High level event handlers onPostInit: null, // Callback(isReloading, isError) when tree was (re)loaded. onActivate: null, // Callback(dtnode) when a node is activated. onDeactivate: null, // Callback(dtnode) when a node is deactivated. onSelect: null, // Callback(flag, dtnode) when a node is (de)selected. onExpand: null, // Callback(dtnode) when a node is expanded/collapsed. onLazyRead: null, // Callback(dtnode) when a lazy node is expanded for the first time. ajaxDefaults: { // Used by initAjax option cache: false, // false: Append random \'_\' argument to the request url to prevent caching. dataType: \"json\" // Expect json format and pass json object to callbacks. }, strings: { loading: \"Loading…\", loadError: \"Load error!\" }, idPrefix: \"ui-dynatree-id-\", // Used to generate node id\'s like <span id="ui-dynatree-id-\">. // cookieId: \"ui-dynatree-cookie\", // Choose a more unique name, to allow multiple trees. cookieId: \"dynatree\", // Choose a more unique name, to allow multiple trees. cookie: { expires: null //7, // Days or Date; null: session cookie // path: \"/\", // Defaults to current page // domain: \"jquery.com\", // secure: true }, // Class names used, when rendering the HTML markup. // Note: if only single entries are passed for options.classNames, all other // values are still set to default. classNames: { container: \"ui-dynatree-container\", folder: \"ui-dynatree-folder\", document: \"ui-dynatree-document\", empty: \"ui-dynatree-empty\", vline: \"ui-dynatree-vline\", expander: \"ui-dynatree-expander\", connector: \"ui-dynatree-connector\", checkbox: \"ui-dynatree-checkbox\", nodeIcon: \"ui-dynatree-icon\", title: \"ui-dynatree-title\", nodeError: \"ui-dynatree-statusnode-error\", nodeWait: \"ui-dynatree-statusnode-wait\", hidden: \"ui-dynatree-hidden\", combinedExpanderPrefix: \"ui-dynatree-exp-\", combinedIconPrefix: \"ui-dynatree-ico-\", // disabled: \"ui-dynatree-disabled\", hasChildren: \"ui-dynatree-has-children\", active: \"ui-dynatree-active\", selected: \"ui-dynatree-selected\", expanded: \"ui-dynatree-expanded\", lazy: \"ui-dynatree-lazy\", focused: \"ui-dynatree-focused\", partsel: \"ui-dynatree-partsel\", lastsib: \"ui-dynatree-lastsib\" }, debugLevel: 1, // ------------------------------------------------------------------------ lastentry: undefined }; /** * Reserved data attributes for a tree node. */ $.ui.dynatree.nodedatadefaults = { title: null, // (required) Displayed name of the node (html is allowed here) key: null, // May be used with activate(), select(), find(), ... isFolder: false, // Use a folder icon. Also the node is expandable but not selectable. isLazy: false, // Call onLazyRead(), when the node is expanded for the first time to allow for delayed creation of children. tooltip: null, // Show this popup text. icon: null, // Use a custom image (filename relative to tree.options.imagePath). \'null\' for default icon, \'false\' for no icon. addClass: null, // Class name added to the node\'s span tag. activate: false, // Initial active status. focus: false, // Initial focused status. expand: false, // Initial expanded status. select: false, // Initial selected status. hideCheckbox: false, // Suppress checkbox display for this node. unselectable: false, // Prevent selection. // disabled: false, // The following attributes are only valid if passed to some functions: children: null, // Array of child nodes. // NOTE: we can also add custom attributes here. // This may then also be used in the onActivate(), onSelect() or onLazyTree() callbacks. // ------------------------------------------------------------------------ lastentry: undefined }; // --------------------------------------------------------------------------- })(jQuery); // Eclipse syntax parser breaks on this expression, so we put it at the bottom. var _rexDtLibName = /.*dynatree[^/]*.js$/i; Can you help with this. Im sure it will help me from asking more questions if i can learn now to add this i could do so with others on my own.
    • You may try add following code in function myControls in jpolite.ext.js:

      function myControls(){
      //Assign Controls handlers to selectors
      $.addControls({

      “div.dynatree”: [$.fn.dynatree],

      Replace div.dynatree with ANY valid selector, ID or class.
      Better a class if you need dynatree to appear in multiple modules, for ID is supposedly unique throughout the entire document.

  12. Hello just getting back to working on this again.

    One thing ive noticed is that modules do not seem to wrap text.
    I get a over flow.
    Is this a known issue about the module pages and how they display on the web.

    I guess you can call it word wrap or wrapping of text within a container.

    Are there any work around other then making the module widths wider?

    • Not quite sure about the situation you encounter, please double-check whether you wrap text in other elements with width set wider than the module, in that case, change the CSS setting for those elements.

  13. Yeah ive noticed that its a bit slow and as i add just normal content to Jpolite its getting slower.
    Its not the site it has good bandwith.
    Its a good container but as with all containers there are limitations to what you can put in them. Everything has a cause and effect scenario.
    Im going to have to redo somethings and see if i can unbog it a bit.
    Gritter is a big problem i know this already. To many messages and Jpolite takes a pounding.
    Ill let you know what i can figure out on what to do.

    • Gritter is optional, you can just remove related CSS & JS from index.html, and change the definition of function $.alert atop jpolite.core.js.

      For the slowness, you may consider removing the transition effect when switching between tabs. Find in jpolite.core.js the following lines:
      t1: $.fn.fadeOut, //Content transition out function
      t2: $.fn.fadeIn, //Content transition in function

      and change it into something like
      t1: $.fn.hide,
      t2: $.fn.show,

      and the transition should work faster đŸ™‚
      Alternatively, you can set the transitions in jpolite.ext.js so as to keep the core JS untouched.

  14. Hi, first I want to congratulate you for such a wonderful Ajax Portal FrameWork.

    My problem is the following, I want to retrieve dynamic generated content as a module/nnn.html, so i just replaced in modules.js to test, but I cannot get updated content, it seems it is being cached.

    I added header(“Cache-Control: no-store, no-cache, must-revalidate”); to my php script, but no success.

    Is there anyway I can allow non cached content if ?

    Thanks by advance. Regards.

    • JPolite uses jQuery’s load() function to retrieve Ajax content, in case you wanna control cache, there are two possible solutions:
      1) Replace .load() with $.ajax({ … cache:false …}) to force no-cache
      2) Still use load() but attach a random number parameter on each request, e.g., load(“test.html?timer=”+ (new Date()).getTime())

      BTW: not sure which version you’re working on, but I suggest you work with V2.

      • Thank you, I will work with V2 then, but I’d like to know how to disable the notification, is obstrusive sometimes.

    • Since I’ve got some similar requests recently I’ll compile related info into a whole doc, hopefully by this weekend đŸ™‚

    • Nishal, you can use it any way you wish đŸ™‚

      Just one suggestion, better leave the jpolite.core.js untouched, and make all your custom code in jpolite.ext.js

  15. Hello,

    Thanks a lot for providing this wonderful light-weight framework!

    Can you please let me know how to go about implementing some authorisation (typical Login & logout functionality with user credentials stored using Cookie) functionality to this? I see some Login code in ‘jpolite.ext.js’ but not sure how to use it.

    Thanks in advance,
    Anand

    • Anand,

      It’s a mistake to leave the login code there đŸ˜€ since it’s for a personal project based on jpolite.
      Depending on your backend (PHP, Ruby, JSP) there will be various existing authorization solutions, which is beyond jpolite’s concern đŸ™‚

    • Rob,
      You may try to build a simple web site with your favorite tech, PHP, JSP, Ruby … then customize the dynamic pages to output addressible HTML segments (not full page, i.e., without HTML, HEADER or BODY tags), and link the source of the modules to those segments, and you’ll get what you need!

  16. For some reason my modules are showing up, shown below is the modules.js file:

    var _modules={
    mMission:{url:”modules/mMission.html”, t:”Mission”},
    mCharts:{url:”modules/mCharts.html”, t:”Charts”},
    };

    var _moduleLayout={
    charts:[“mCharts:c2”],
    home1:[“mMission:c1”, “mCharts:c2”]
    };

    • @Michael,

      Please make sure “charts” & “home1” are actual IDs of LI members in your index.html#main_nav section, i.e., they should link to existing DOM elements, otherwise they won’t work.

      BTW: my naming system t1,t2,t3 … m101,m102,m103 … c1,c2,c3 may not seem so obvious and convenient for everybody, but it is a simple symbolic system “function + number” that makes it easy to manage. If you prefer memorable IDs, better use some prefix e.g., m_XYZ for modules and t_ABC for tabs.

  17. Hello,

    First off all your Plugin is awesome, great job…

    1. How do I allow the modules container (.container currently set to 950) to be 100% width, like how it is on netvibes where modules are end to end i.e the whole width of the screen. if I set

    #container in style.css to wdth:100% it starts from the left most end, but doesnt go all the way to the right. it stops where the “adjust module appearance” arrows are. I want

    2. Where in the code can I modify how modules switch and drag. when I drag a module over another I want them to switch positions.. again like the netvibes website.

    Thanks

    • @Roye,

      To modify the container width, you can set #content width=100% in style(0/1).css, you can find a remark there.
      Then go to modules.js and modify _columnLayout to change the width of different modules under different tabs. The value you see there in the sample e.g., c1:”span-24″ are class names defined in bluetrip grid, which are all fixed width. If you wanna the container width to be 100% and each module to appear 1/3, just define a new custom class name, e.g., oneThird {width:33%}, and assign the value to a column, e.g., c1:”oneThird”, and all modules in that column will be 1/3 of container width

      About drag and drop, I actually didn’t do anything specific there, I just used jQuery UI’s code. You may check out their latest version for possible improvements.

  18. I have been trying to use a chart from highcharts.com as the content for widgets, was not successful. Anyone tried it? Or give guidance.

    • @Eddie,

      As I can see from highcharts document, it takes an ID to render charts. However, that’s for a static page with desired IDs already in place. JPolite is a dynamic environment where the desired ID is not necessarily loaded at the beginning, that you can’t use $(document).ready(…)

      Do the following instead:
      1.Find jpolite.ext.js, insert a line in function myControls() within $.regControls({…})
      “.highcharts”: [$.fn.InitHighCharts],
      “.highcharts” is an arbitrary class name to identify elements for highcharts in any module
      2.Add a jQuery plugin method $.fn.InitHighCharts,
      $.fn.InitHighCharts = function(){
      return this.each(function(){
      new Highcharts.Chart({
      chart: {
      renderTo: this.id, //this.id is the element to show the chart, e.g., like with class=”highcharts”
      defaultSeriesType: ‘bar’
      }, …
      });
      }
      3. (Optional) adopt any strategy to provide different data to different charts

  19. Hi,

    Really love the framework. Want to use it in a project we have coming up.

    However, the drag & drop doesn’t seem to be working properly. Moving a panel back to its original space doesn’t seem to be allowed. In general movement of the modules seems difficult.

    Is this a browser based issue or are there any fixes for this?

    Thanks,
    Scott

    • I just make use of jQuery UI to handle drop & drop, at the time it was version 1.72.
      I just tried latest build V1.8.12, which works way better.

      • Hey Trilancer,

        Great portal framework! I did have one question. How did you upgrade the jquery-ui bundle? I replaced the default 1.72 with 1.8.13 in the section, however, nothing renders now. Any tips?

  20. I figured it out. Make sure you update the jquery.js file along with the jquery-ui.js file. Thanks again trilancer!

  21. Great plug-in.

    For the next version you may want to consider making page (tab) titles part of the module layout string. This makes persistence of dynamic, user-defined pages a little easier.

    • @Ralph:

      By design, JPolite is NOT SEO friendly, so setting tab titles in HTML is more or less helpful for search engines đŸ™‚
      Anyway, I’ll try to build in some flexibility that to support tab titles from both HTML AND configuration file.

      • Thanks for the response.

        I had updated the jPolite plug-in to allow users to create their own pages (tabs) and put modules on the new pages.

        The user defined tab titles are stored in a db. The tab titles are matched up with the layout string tab id’s on the server side as the jPolite page loads. In other words the “main_nav” li items are generated on the server. Seems to work ok.

        Also for the next version it may be nice to store the module state (minimized or normal size) so that modules will still be minimized when the user returns to the jPolite page.

      • Flexibility to load/store tab/module data will be seriously considered for sure đŸ˜€

      • Just found that persisting module min/max state in V2 is pretty easy:

        * Add a new property in the core loadLayout function ( e.g. ms: s[4] || ”)
        * Append this new property to the core saveLayout function (e.g. , m[i].ms,)
        * Check this new property in the core addModule function (e.g. if (m.ms === ‘min’){t.modules[m.id].min();} )
        * Update the myLiveEvents function in the ext.js file to update the new property & save layout
        (e.g. $(this).parents(“.module”)[0].ms = ‘min’; [call function to save layout] )

  22. What would be a recommended way to refresh an individual module from script?

    This is what I came up with to refresh a module on the current tab. There is probably a better way.

    $(“#m” + moduleId + ” b”).filter(“.actionRefresh”).parents(“.module”).each(function(i)
    {
    if (this.tab.id === $.jpolite.Nav.ct)
    {
    this.loadContent(true);
    }
    });

    Where [moduleId] is the id of the module that you want to refresh.

  23. i have made a login page…after processing at server side using php ,i woul like to navigated to home page…

    ho this can be done ?

    • @kishan,

      I’d suggest you use two different pages, one for login, the other for actual content layout, and let server side check authentication:

      1.If user opens the login page URL with authentication, then redirect to the content page directly.
      2.If user opens content page without authentication, redirect back to login page.

      This is a simplified solution, otherwise you may need to provide two different layout for users with or without authentication.

    • One simplest way is to make another layout file, e.g., modules2.js, and another html file, say index2.html.
      And let index2.html load modules2.js as layout.

  24. Hi trilancer,

    Thanks for this smart and inspiring little framework.

    I’ve been toying with jPolite since a while, first as a photo blog : http://www.casualpictures.net/
    and then as an ultra bookmark interface for online shopping addicts : http://www.skewee.com/shopping.php
    (Just uploaded).

    Main extensions to jPolite2 :

    1) the html modules are showing part of an external iFrame.
    2) new jQuery UI icons and functions have been added to module headers (jpolite.ext.js – function myLiveEvents()):
    . Scroll
    . Settings
    . Show web link
    Scroll values and settings (title and iFrame url) are stored either in cookies (if not logged in), or in MySQL values associated with the user account.
    3) PHP / MySQL authentication is implemented in an external module : http://www.skewee.com/registration/, with redirection to the content page if login is successful
    4) index.html has been migrated to shopping.php

    User documentation is available from main page (http://www.skewee.com/).

    Please enjoy.

  25. The jpolite demo URL is not working. Are you still working on the future versions ? Where can I see the demo of JP1, JP2 and JP3 ?

Leave a reply to Yosep Cancel reply