1 /** 2 * Base definition file for the JSWidgets project. 3 * This project defines a widget structure that allows users to create very robust, 4 * interactive web pages constructed of modular widgets with little overhead. 5 * 6 * @fileoverview 7 * @author Garth Henson (<a href="http://www.guahanweb.com">Guahan Web</a>) 8 * @version 0.1 9 */ 10 11 var JSWidgets = Class.extend( 12 /** @lends JSWidgets.prototype */ 13 { 14 /** 15 * This object allows for the easy management of existing widgets. While individual widgets 16 * can easily be created and maintained manually, using the JSWidgets namespace as a 17 * manager helps clean up the structure and aids with looping over all existing widgets. 18 * @constructs 19 * @author Garth Henson (<a href="http://www.guahanweb.com">Guahan Web</a>) 20 * @class This is the management system for handling all widget objects currently created. 21 * @version 0.1 22 * 23 * @example 24 * // Start new manager 25 * var widgets = new JSWidgets(); 26 * 27 * // Add a loader widget to the manager 28 * var loader = widgets.add(new LoaderWidget({ 29 * title : 'Loader Widget' 30 * }); 31 */ 32 construct : function() { 33 this.curr_id = 0; 34 this.widgets = new Array(); 35 this.initialized = true; 36 }, 37 38 /** 39 * Add the provided widget to this collection. If a callback is defined, it will be executed with the following parameters: 40 * <ul> 41 * <li>{boolean} <b>success</b> Whether or not the widget was added successfully</li> 42 * <li>{string} <b>error</b> Upon failure, and error string will be provided</li> 43 * </ul> 44 * @param {Widget} w A {@link BaseWidget} or other valid child of that class 45 * @param {function} callback An optional function to execute upon successful addition. 46 * @returns {Widget} The widget passed in to be added 47 */ 48 add : function(w, callback) { 49 var that = this; 50 w.setManager(this); 51 var action = function() { 52 if (w.valid()) { 53 that.widgets[that.curr_id] = w; 54 w.setUid(that.curr_id); 55 that.curr_id++; 56 if (callback || false) { 57 callback(true); 58 } 59 } else { 60 var msg = "JSWidets error: Invalid widget object added!"; 61 if (callback || false) { 62 callback(false, msg); 63 } else { 64 throw msg; 65 } 66 } 67 } 68 69 if (DEBUG || false) { 70 // During debug, randomize a delay on loading 71 var delay = Math.round(Math.random() * 1500); 72 setTimeout(action, delay); 73 } else { 74 action(); 75 } 76 77 return w; 78 }, 79 80 /** 81 * Removes the widget with the provided <code>uid</code> and destroys it from the DOM. 82 * @public 83 * @param {string} uid The <code>uid</code> to be removed 84 */ 85 remove : function(uid) { 86 for (var i = 0; i < this.widgets.length; i++) { 87 var w = this.widgets[i]; 88 if (w.uid == uid) { 89 this.widgets.splice(i, 1); 90 w.destroy(); 91 } 92 } 93 } 94 }); 95 96 /** 97 * Load event is triggered when the widget has completed loading. 98 * @name BaseWidget#load 99 * @event 100 * @param {BaseWidget} w The widget object which was loaded 101 */ 102 103 var BaseWidget = Class.extend( 104 /** @lends BaseWidget.prototype */ 105 { 106 /** 107 * The name of the current widget (should be overwritten by all child classes). 108 * @type String 109 */ 110 name : 'JSWidget Base', 111 112 /** 113 * Configuration of the current widget. This is loaded by the <code>opts</code> parameter in the 114 * object constructor. Valid configuration options are: 115 * <ul> 116 * <li><b>listeners</b> - event handlers for the following custom events: 117 * <ul> 118 * <li><b>load</b> - executed once the widget has loaded</li> 119 * </ul> 120 * </li> 121 * <li><b>title_selector</b> - defaults to '.title'</li> 122 * <li><b>content_selector</b> - defaults to '.content'</li> 123 * <li><b>title</b> - defaults to the widget name</li> 124 * </ul> 125 * 126 * @type Object 127 */ 128 config : { 129 closeable : false 130 }, 131 132 /** 133 * This is the base widget from which all others extend 134 * @author Garth Henson (<a href="http://www.guahanweb.com">Guahan Web</a>) 135 * @constructs 136 * @class This is the object that defines the base functionality from which all other JSWidgets will extend. 137 * @version 0.1 138 * 139 * @param {string} id The div (or element) ID attribute of the widget 140 * @param {JSWidgets} mgr The {@link JSWidgets} manager to which this widget should be assigned 141 * @param {object} opts Additional options for the widget 142 */ 143 construct : function(id, opts) { 144 this.id = '#' + id; 145 this.is_valid = this.validate(opts); 146 this.config = opts || {}; 147 this.listeners = opts['listeners'] || {}; 148 this.manager = null; 149 150 // Set up selectors 151 this.title_selector = this.config.title_selector || '.title'; 152 this.content_selector = this.config.content_selector || '.content'; 153 154 // Default constructor actions 155 this.setTitle(); 156 this.setUp(); 157 if (this.listeners.load) { 158 this.listeners.load(this); 159 } 160 }, 161 162 /** 163 * Destroy the current widget from the DOM and cleanup 164 * @public 165 */ 166 destroy : function() { 167 var that = this; 168 $(this.id).fadeOut('slow', function() { 169 $(that.id).remove(); 170 }); 171 }, 172 173 /** 174 * Appends the content as a new line to the widget's content area 175 * @public 176 * @param {string} val The value to append 177 */ 178 appendContent : function(val) { 179 var el = $(this.id).find(this.content_selector); 180 var txt = el.html(); 181 if (txt == '') { 182 el.html(val); 183 } else { 184 el.html(txt + '<br />' + val); 185 } 186 }, 187 188 /** 189 * Defines the content to replace into the current widget 190 * @public 191 * @param {String} val The raw markup to be inserted into the content region 192 */ 193 setContent : function(val) { 194 $(this.id).find(this.content_selector).html(val); 195 }, 196 197 /** 198 * Set the object's UID to the provided <code>id</code> 199 * @public 200 * @param {int} id The new UID 201 */ 202 setUid : function(id) { 203 this.uid = id; 204 }, 205 206 /** 207 * Set the content for the title region of the widget 208 * @public 209 * @param {string} val The new title to display 210 */ 211 setTitle : function(val) { 212 var title = val || this.getTitle(); 213 $(this.id).find(this.title_selector).html(title); 214 }, 215 216 /** 217 * Runs the initial required setup for this widget (including creating the <code>close</code> button if required). 218 * @private 219 */ 220 setUp : function() { 221 var that = this; 222 223 // Container needs to be relative to house all child elements 224 $(this.id).css('position', 'relative'); 225 226 // If closeable, generate our close link 227 if (this.config.closeable) { 228 $(this.id).append('<a href="#" class="jswidget-gen-close">close</a>'); 229 $(this.id).find('a.jswidget-gen-close').css({ 230 position: 'absolute', 231 top: '10px', 232 right : '10px' 233 }).click(function(e) { 234 e.preventDefault(); 235 that.unregister(); 236 }); 237 } 238 }, 239 240 setManager : function(mgr) { 241 if (mgr instanceof JSWidgets) { 242 this.manager = mgr; 243 } 244 }, 245 246 /** 247 * Returns the configured title of this widget. Defaults to the widget name if no title has been set. 248 * @public 249 * @returns {string} The widget's title 250 */ 251 getTitle : function() { 252 return (this.config.title || this.name); 253 }, 254 255 /** 256 * An accessor method that allows this widget to remove itself from the manager implicitly. 257 * @public 258 */ 259 unregister : function() { 260 if (this.manager || false) { 261 this.manager.remove(this.uid); 262 } 263 }, 264 265 /** 266 * Validate the bare minimum opts are present 267 * This method should be overridden by all widgets 268 * @public 269 * @returns {boolean} Whether or not this widget is valid 270 */ 271 validate : function(opts) { return true; }, 272 273 /** 274 * 275 */ 276 valid : function() { 277 return this.is_valid || false; 278 } 279 }); 280