1/** 2 * TableDnD plug-in for JQuery, allows you to drag and drop table rows 3 * You can set up various options to control how the system will work 4 * Copyright (c) Denis Howlett <[email protected]> 5 * Licensed like jQuery, see http://docs.jquery.com/License. 6 * 7 * Configuration options: 8 * 9 * onDragStyle 10 * This is the style that is assigned to the row during drag. There are limitations to the styles that can be 11 * associated with a row (such as you can't assign a border--well you can, but it won't be 12 * displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as 13 * a map (as used in the jQuery css(...) function). 14 * onDropStyle 15 * This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations 16 * to what you can do. Also this replaces the original style, so again consider using onDragClass which 17 * is simply added and then removed on drop. 18 * onDragClass 19 * This class is added for the duration of the drag and then removed when the row is dropped. It is more 20 * flexible than using onDragStyle since it can be inherited by the row cells and other content. The default 21 * is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your 22 * stylesheet. 23 * onDrop 24 * Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table 25 * and the row that was dropped. You can work out the new order of the rows by using 26 * table.rows. 27 * onDragStart 28 * Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the 29 * table and the row which the user has started to drag. 30 * onAllowDrop 31 * Pass a function that will be called as a row is over another row. If the function returns true, allow 32 * dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under 33 * the cursor. It returns a boolean: true allows the drop, false doesn't allow it. 34 * scrollAmount 35 * This is the number of pixels to scroll if the user moves the mouse
35cursor to the top or bottom of the 36 * window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2, 37 * FF3 beta 38 * dragHandle 39 * This is the name of a class that you assign to one or more cells in each row that is draggable. If you 40 * specify this class, then you are responsible for setting cursor: move in the CSS and only these cells 41 * will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where 42 * the whole row is draggable. 43 * 44 * Other ways to control behaviour: 45 * 46 * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows 47 * that you don't want to be draggable. 48 * 49 * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form 50 * <tableID>[]=<rowID1>&<tableID>[]=<rowID2> so that you can send this back to the server. The table must have 51 * an ID as must all the rows. 52 * 53 * Other methods: 54 * 55 * $("...").tableDnDUpdate() 56 * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells). 57 * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again. 58 * The table maintains the original configuration (so you don't have to specify it again). 59 * 60 * $("...").tableDnDSerialize() 61 * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be 62 * called from anywhere and isn't dependent on the currentTable being set up correctly before calling 63 * 64 * Known problems: 65 * - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0 66 * 67 * Version 0.2: 2008-02-20 First public version 68 * Version 0.3: 2008-02-07 Added onDragStart option 69 * Made the scroll amount configurable (default is 5 as before) 70 * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes 71 * Added onAllowDrop to control dropping 72 * Fixed a bug which meant that you couldn't set the scroll amount in both directions 73 * Added serialize method 74 * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row 75 * draggable 76 * Improved the serialize method to use a default (and settable) regular expression. 77 * Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table 78 */ 79jQuery.tableDnD = { 80 /** Keep hold of the current table being dragged */ 81 currentTable : null, 82 /** Keep hold of the current drag object if any */ 83 dragObject: null, 84 /** The current mouse offset */ 85 mouseOffset: null, 86 /** Remember the old value of Y so that we don't do too much processing */ 87 oldY: 0, 88 89 /** Actually build the structure */ 90 build: function(options) { 91 // Set up the defaults if any 92 93 this.each(function() { 94 // This is bound to each matching table, set up the defaults and override with user options 95 this.tableDnDConfig = jQuery.extend({ 96 onDragStyle: null, 97 onDropStyle: null, 98 // Add in the default class for whileDragging 99 onDragClass: "tDnD_whileDrag", 100 onDrop: null, 101 onDragStart: null, 102 scrollAmount: 5, 103 serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs 104 serializeParamName: null, // If you want to specify another parameter name instead of the table ID 105 dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable 106 }, options || {}); 107 // Now make the rows draggable 108 jQuery.tableDnD.makeDraggable(this); 109 }); 110 111 // Now we need to capture the mouse up and mouse move event 112 // We can use bind so that we don't interfere with other event handlers 113 jQuery(document) 114 .bind('mousemove', jQuery.tableDnD.mousemove) 115 .bind('mouseup', jQuery.tableDnD.mouseup); 116 117 // Don't break the chain 118 return this; 119 }, 120 121 /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */ 122 makeDraggable: function(table) { 123 var config = table.tableDnDConfig; 124 if (table.tableDnDConfig.dragHandle) { 125 // We only need to add the event to the specified cells 126 var cells = jQuery("td."+table.tableDnDConfig.dragHandle, table); 127 cells.each(function() { 128 // The cell is bound to "this" 129 jQuery(this).mousedown(function(ev) { 130 jQuery.tableDnD.dragObject = this.parentNode; 131 jQuery.tableDnD.currentTable = table; 132 jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev); 133 if (config.onDragStart) { 134 // Call the onDrop method if there is one 135 config.onDragStart(table, this); 136 } 137 return false;
138 }); 139 }) 140 } else { 141 // For backwards compatibility, we add the event to the whole row 142 var rows = jQuery("tr", table); // get all the rows as a wrapped set 143 rows.each(function() { 144 // Iterate through each row, the row is bound to "this" 145 var row = jQuery(this); 146 if (! row.hasClass("nodrag")) { 147 row.mousedown(function(ev) { 148 if (ev.target.tagName == "TD") { 149 jQuery.tableDnD.dragObject = this; 150 jQuery.tableDnD.currentTable = table; 151 jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev); 152 if (config.onDragStart) { 153 // Call the onDrop method if there is one 154 config.onDragStart(table, this); 155 } 156 return false; 157 } 158 }).css("cursor", "move"); // Store the tableDnD object 159 } 160 }); 161 } 162 }, 163 164 updateTables: function() { 165 this.each(function() { 166 // this is now bound to each matching table 167 if (this.tableDnDConfig) { 168 jQuery.tableDnD.makeDraggable(this); 169 } 170 }) 171 }, 172 173 /** Get the mouse coordinates from the event (allowing for browser differences) */ 174 mouseCoords: function(ev){ 175 if(ev.pageX || ev.pageY){ 176 return {x:ev.pageX, y:ev.pageY}; 177 } 178 return { 179 x:ev.clientX + document.body.scrollLeft - document.body.clientLeft, 180 y:ev.clientY + document.body.scrollTop - document.body.clientTop 181 }; 182 }, 183 184 /** Given a target element and a mouse event, get the mouse offset from that element. 185 To do this we need the element's position and the mouse position */ 186 getMouseOffset: function(target, ev) { 187 ev = ev || window.event; 188 189 var docPos = this.getPosition(target); 190 var mousePos = this.mouseCoords(ev); 191 return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y}; 192 }, 193 194 /** Get the position of an element by going up the DOM tree and adding up all the offsets */ 195 getPosition: function(e){ 196 var left = 0; 197 var top = 0; 198 /** Safari fix -- thanks to Luis Chato for this! */ 199 if (e.offsetHeight == 0) { 200 /** Safari 2 doesn't correctly grab the offsetTop of a table row 201 this is detailed here: 202 http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/ 203 the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild. 204 note that firefox will return a text node as a first child, so designing a more thorough 205 solution may need to take that into account, for now this seems to work in firefox, safari, ie */ 206 e = e.firstChild; // a table cell 207 } 208 209 while (e.offsetParent){ 210 left += e.offsetLeft; 211 top += e.offsetTop; 212 e = e.offsetParent; 213 } 214 215 left += e.offsetLeft; 216 top += e.offsetTop; 217 218 return {x:left, y:top}; 219 }, 220 221 mousemove: function(ev) { 222 if (jQuery.tableDnD.dragObject == null) { 223 return; 224 } 225 226 var dragObj = jQuery(jQuery.tableDnD.dragObject); 227 var config = jQuery.tableDnD.currentTable.tableDnDConfig; 228 var mousePos = jQuery.tableDnD.mouseCoords(ev); 229 var y = mousePos.y - jQuery.tableDnD.mouseOffset.y; 230 //auto scroll the window 231 var yOffset = window.pageYOffset; 232 if (document.all) {
233 // Windows version 234 //yOffset=document.body.scrollTop; 235 if (typeof document.compatMode != 'undefined' && 236 document.compatMode != 'BackCompat') { 237 yOffset = document.documentElement.scrollTop; 238 } 239 else if (typeof document.body != 'undefined') { 240 yOffset=document.body.scrollTop; 241 } 242 243 } 244 245 if (mousePos.y-yOffset < config.scrollAmount) { 246 window.scrollBy(0, -config.scrollAmount); 247 } else { 248 var windowHeight = window.innerHeight ? window.innerHeight 249 : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight; 250 if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) { 251 window.scrollBy(0, config.scrollAmount); 252 } 253 } 254 255 256 if (y != jQuery.tableDnD.oldY) { 257 // work out if we're going up or down... 258 var movingDown = y > jQuery.tableDnD.oldY; 259 // update the old value 260 jQuery.tableDnD.oldY = y; 261 // update the style to show we're dragging 262 if (config.onDragClass) { 263 dragObj.addClass(config.onDragClass); 264 } else { 265 dragObj.css(config.onDragStyle); 266 } 267 // If we're over a row then move the dragged row to there so that the user sees the 268 // effect dynamically 269 var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y); 270 if (currentRow) { 271 // TODO worry about what happens when there are multiple TBODIES 272 if (movingDown && jQuery.tableDnD.dragObject != currentRow) { 273 jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling); 274 } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) { 275 jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow); 276 } 277 } 278 } 279 280 return false; 281 }, 282 283 /** We're only worried about the y position really, because we can only move rows up and down */ 284 findDropTargetRow: function(draggedRow, y) { 285 var rows = jQuery.tableDnD.currentTable.rows; 286 for (var i=0; i<rows.length; i++) { 287 var row = rows[i]; 288 var rowY = this.getPosition(row).y; 289 var rowHeight = parseInt(row.offsetHeight)/2; 290 if (row.offsetHeight == 0) { 291 rowY = this.getPosition(row.firstChild).y; 292 rowHeight = parseInt(row.firstChild.offsetHeight)/2; 293 } 294 // Because we always have to insert before, we need to offset the height a bit 295 if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) { 296 // that's the row we're over 297 // If it's the same as the current row, ignore it 298 if (row == draggedRow) {return null;} 299 var config = jQuery.tableDnD.currentTable.tableDnDConfig; 300 if (config.onAllowDrop) { 301 if (config.onAllowDrop(draggedRow, row)) { 302 return row; 303 } else { 304 return null; 305 } 306 } else { 307 // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic) 308 var nodrop = jQuery(row).hasClass("nodrop"); 309 if (! nodrop) { 310 return row; 311 } else { 312 return null; 313 } 314 } 315 return row; 316 } 317 } 318 return null; 319 }, 320 321 mouseup: function(e) { 322 if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) { 323 var droppedRow = jQuery.tableDnD.dragObject; 324 var config = jQuery.tableDnD.currentTable.tableDnDConfig; 325 // If we have a dragObject, then we need to release it, 326 // The row will already have been moved to the right place so we just reset stuff 327 if (config.onDragClass) {
328 jQuery(droppedRow).removeClass(config.onDragClass); 329 } else { 330 jQuery(droppedRow).css(config.onDropStyle); 331 } 332 jQuery.tableDnD.dragObject = null; 333 if (config.onDrop) { 334 // Call the onDrop method if there is one 335 config.onDrop(jQuery.tableDnD.currentTable, droppedRow); 336 } 337 jQuery.tableDnD.currentTable = null; // let go of the table too 338 } 339 }, 340 341 serialize: function() { 342 if (jQuery.tableDnD.currentTable) { 343 return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable); 344 } else { 345 return "Error: No Table id set, you need to set an id on your table and every row"; 346 } 347 }, 348 349 serializeTable: function(table) { 350 var result = ""; 351 var tableId = table.id; 352 var rows = table.rows; 353 for (var i=0; i<rows.length; i++) { 354 if (result.length > 0) result += "&"; 355 var rowId = rows[i].id; 356 if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) { 357 rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0]; 358 } 359 360 result += tableId + '[]=' + rowId; 361 } 362 return result; 363 }, 364 365 serializeTables: function() { 366 var result = ""; 367 this.each(function() { 368 // this is now bound to each matching table 369 result += jQuery.tableDnD.serializeTable(this); 370 }); 371 return result; 372 } 373 374} 375 376jQuery.fn.extend( 377 { 378 tableDnD : jQuery.tableDnD.build, 379 tableDnDUpdate : jQuery.tableDnD.updateTables, 380 tableDnDSerialize: jQuery.tableDnD.serializeTables 381 } 382);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.