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 this.bind('mousemove', jQuery.tableDnD.mousemove).bind('mouseup', jQuery.tableDnD.mouseup); 114 115 // Don't break the chain 116 return this; 117 }, 118 119 /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */ 120 makeDraggable: function(table) { 121 var config = table.tableDnDConfig; 122 if (table.tableDnDConfig.dragHandle) { 123 // We only need to add the event to the specified cells 124 var cells = jQuery("td."+table.tableDnDConfig.dragHandle, table); 125 cells.each(function() { 126 // The cell is bound to "this" 127 jQuery(this).mousedown(function(ev) { 128 jQuery.tableDnD.dragObject = this.parentNode; 129 jQuery.tableDnD.currentTable = table; 130 jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev); 131 if (config.onDragStart) { 132 // Call the onDrop method if there is one 133 config.onDragStart(table, this); 134 } 135 return false;
136 }); 137 }) 138 } else { 139 // For backwards compatibility, we add the event to the whole row 140 var rows = jQuery("tr", table); // get all the rows as a wrapped set 141 rows.each(function() { 142 // Iterate through each row, the row is bound to "this" 143 var row = jQuery(this); 144 if (! row.hasClass("nodrag")) { 145 row.mousedown(function(ev) { 146 if (ev.target.tagName == "TD") { 147 jQuery.tableDnD.dragObject = this; 148 jQuery.tableDnD.currentTable = table; 149 jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev); 150 if (config.onDragStart) { 151 // Call the onDrop method if there is one 152 config.onDragStart(table, this); 153 } 154 return false; 155 } 156 }).css("cursor", "move"); // Store the tableDnD object 157 } 158 }); 159 } 160 }, 161 162 updateTables: function() { 163 this.each(function() { 164 // this is now bound to each matching table 165 if (this.tableDnDConfig) { 166 jQuery.tableDnD.makeDraggable(this); 167 } 168 }) 169 }, 170 171 /** Get the mouse coordinates from the event (allowing for browser differences) */ 172 mouseCoords: function(ev){ 173 if(ev.pageX || ev.pageY){ 174 return {x:ev.pageX, y:ev.pageY}; 175 } 176 return { 177 x:ev.clientX + document.body.scrollLeft - document.body.clientLeft, 178 y:ev.clientY + document.body.scrollTop - document.body.clientTop 179 }; 180 }, 181 182 /** Given a target element and a mouse event, get the mouse offset from that element. 183 To do this we need the element's position and the mouse position */ 184 getMouseOffset: function(target, ev) { 185 ev = ev || window.event; 186 187 var docPos = this.getPosition(target); 188 var mousePos = this.mouseCoords(ev); 189 return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y}; 190 }, 191 192 /** Get the position of an element by going up the DOM tree and adding up all the offsets */ 193 getPosition: function(e){ 194 var left = 0; 195 var top = 0; 196 /** Safari fix -- thanks to Luis Chato for this! */ 197 if (e.offsetHeight == 0) { 198 /** Safari 2 doesn't correctly grab the offsetTop of a table row 199 this is detailed here: 200 http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/ 201 the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild. 202 note that firefox will return a text node as a first child, so designing a more thorough 203 solution may need to take that into account, for now this seems to work in firefox, safari, ie */ 204 e = e.firstChild; // a table cell 205 } 206 207 while (e.offsetParent){ 208 left += e.offsetLeft; 209 top += e.offsetTop; 210 e = e.offsetParent; 211 } 212 213 left += e.offsetLeft; 214 top += e.offsetTop; 215 216 return {x:left, y:top}; 217 }, 218 219 mousemove: function(ev) { 220 if (jQuery.tableDnD.dragObject == null) { 221 return; 222 } 223 224 var dragObj = jQuery(jQuery.tableDnD.dragObject); 225 var config = jQuery.tableDnD.currentTable.tableDnDConfig; 226 var mousePos = jQuery.tableDnD.mouseCoords(ev); 227 var y = mousePos.y - jQuery.tableDnD.mouseOffset.y; 228 //auto scroll the window 229 var yOffset = window.pageYOffset; 230 if (document.all) {
231 // Windows version 232 //yOffset=document.body.scrollTop; 233 if (typeof document.compatMode != 'undefined' && 234 document.compatMode != 'BackCompat') { 235 yOffset = document.documentElement.scrollTop; 236 } 237 else if (typeof document.body != 'undefined') { 238 yOffset=document.body.scrollTop; 239 } 240 241 } 242 243 if (mousePos.y-yOffset < config.scrollAmount) { 244 window.scrollBy(0, -config.scrollAmount); 245 } else { 246 var windowHeight = window.innerHeight ? window.innerHeight 247 : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight; 248 if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) { 249 window.scrollBy(0, config.scrollAmount); 250 } 251 } 252 253 254 if (y != jQuery.tableDnD.oldY) { 255 // work out if we're going up or down... 256 var movingDown = y > jQuery.tableDnD.oldY; 257 // update the old value 258 jQuery.tableDnD.oldY = y; 259 // update the style to show we're dragging 260 if (config.onDragClass) { 261 dragObj.addClass(config.onDragClass); 262 } else { 263 dragObj.css(config.onDragStyle); 264 } 265 // If we're over a row then move the dragged row to there so that the user sees the 266 // effect dynamically 267 var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y); 268 if (currentRow) { 269 // TODO worry about what happens when there are multiple TBODIES 270 if (movingDown && jQuery.tableDnD.dragObject != currentRow) { 271 jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling); 272 } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) { 273 jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow); 274 } 275 } 276 } 277 278 return false; 279 }, 280 281 /** We're only worried about the y position really, because we can only move rows up and down */ 282 findDropTargetRow: function(draggedRow, y) { 283 var rows = jQuery.tableDnD.currentTable.rows; 284 for (var i=0; i<rows.length; i++) { 285 var row = rows[i]; 286 var rowY = this.getPosition(row).y; 287 var rowHeight = parseInt(row.offsetHeight)/2; 288 if (row.offsetHeight == 0) { 289 rowY = this.getPosition(row.firstChild).y; 290 rowHeight = parseInt(row.firstChild.offsetHeight)/2; 291 } 292 // Because we always have to insert before, we need to offset the height a bit 293 if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) { 294 // that's the row we're over 295 // If it's the same as the current row, ignore it 296 if (row == draggedRow) {return null;} 297 var config = jQuery.tableDnD.currentTable.tableDnDConfig; 298 if (config.onAllowDrop) { 299 if (config.onAllowDrop(draggedRow, row)) { 300 return row; 301 } else { 302 return null; 303 } 304 } else { 305 // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic) 306 var nodrop = jQuery(row).hasClass("nodrop"); 307 if (! nodrop) { 308 return row; 309 } else { 310 return null; 311 } 312 } 313 return row; 314 } 315 } 316 return null; 317 }, 318 319 mouseup: function(e) { 320 if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) { 321 var droppedRow = jQuery.tableDnD.dragObject; 322 var config = jQuery.tableDnD.currentTable.tableDnDConfig; 323 // If we have a dragObject, then we need to release it, 324 // The row will already have been moved to the right place so we just reset stuff 325 if (config.onDragClass) {
326 jQuery(droppedRow).removeClass(config.onDragClass); 327 } else { 328 jQuery(droppedRow).css(config.onDropStyle); 329 } 330 jQuery.tableDnD.dragObject = null; 331 if (config.onDrop) { 332 // Call the onDrop method if there is one 333 config.onDrop(jQuery.tableDnD.currentTable, droppedRow); 334 } 335 jQuery.tableDnD.currentTable = null; // let go of the table too 336 } 337 }, 338 339 serialize: function() { 340 if (jQuery.tableDnD.currentTable) { 341 return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable); 342 } else { 343 return "Error: No Table id set, you need to set an id on your table and every row"; 344 } 345 }, 346 347 serializeTable: function(table) { 348 var result = ""; 349 var tableId = table.id; 350 var rows = table.rows; 351 for (var i=0; i<rows.length; i++) { 352 if (result.length > 0) result += "&"; 353 var rowId = rows[i].id; 354 if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) { 355 rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0]; 356 } 357 358 result += tableId + '[]=' + rowId; 359 } 360 return result; 361 }, 362 363 serializeTables: function() { 364 var result = ""; 365 this.each(function() { 366 // this is now bound to each matching table 367 result += jQuery.tableDnD.serializeTable(this); 368 }); 369 return result; 370 } 371 372} 373 374jQuery.fn.extend( 375 { 376 tableDnD : jQuery.tableDnD.build, 377 tableDnDUpdate : jQuery.tableDnD.updateTables, 378 tableDnDSerialize: jQuery.tableDnD.serializeTables 379 } 380);
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.