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 a jQuery mach string for one or more cells in each row that is draggable. If you 40 * specify this, 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 * Version 0.6: 2011-12-02 Added support for touch devices 79 * Version 0.7 2012-04-09 Now works with jQuery 1.7 and supports touch, tidied up tabs and spaces 80 */ 81// Determine if this is a touch device 82var hasTouch = 'ontouchstart' in document.documentElement, 83 startEvent = hasTouch ? 'touchstart' : 'mousedown', 84 moveEvent = hasTouch ? 'touchmove' : 'mousemove', 85 endEvent = hasTouch ? 'touchend' : 'mouseup'; 86 87// If we're on a touch device, then wire up the events 88// see http://stackoverflow.com/a/8456194/1316086 89if (hasTouch) { 90 $.each("touchstart touchmove touchend".split(" "), function(i, name) { 91 jQuery.event.fixHooks[name] = jQuery.event.mouseHooks; 92 }); 93 //alert("has Touch"); 94} 95 96jQuery.tableDnD = { 97 /** Keep hold of the current table being dragged */ 98 currentTable : null, 99 /** Keep hold of the current drag object if any */ 100 dragObject: null, 101 /** The current mouse offset */ 102 mouseOffset: null, 103 /** Remember the old value of Y so that we don't do too much processing */ 104 oldY: 0, 105 106 /** Actually build the structure */ 107 build: function(options) { 108 // Set up the defaults if any 109 110 this.each(function() { 111 // This is bound to each matching table, set up the defaults and override with user options 112 this.tableDnDConfig = jQuery.extend({ 113 onDragStyle: null, 114 onDropStyle: null, 115 // Add in the default class for whileDragging 116 onDragClass: "tDnD_whileDrag", 117 onDrop: null, 118 onDragStart: null, 119 scrollAmount: 5, 120 121 serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs 122 serializeParamName: null, // If you want to specify another parameter name instead of the table ID 123 dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable 124 }, options || {}); 125 126 // Now make the rows draggable 127 jQuery.tableDnD.makeDraggable(this); 128 }); 129 130 // Don't break the chain 131 return this; 132 }, 133 134 /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */ 135 makeDraggable: function(table) { 136 137 var config = table.tableDnDConfig; 138 if (config.dragHandle) { 139 // We only need to add the event to the specified cells 140 var cells = jQuery(table.tableDnDConfig.dragHandle, table); 141 cells.each(function() { 142 // The cell is bound to "this" 143 jQuery(this).bind(startEvent, function(ev) { 144 jQuery.tableDnD.initialiseDrag(jQuery(this).parents('tr')[0], table, this, ev, config); 145 return false;
146 }); 147 }) 148 } else { 149 // For backwards compatibility, we add the event to the whole row 150 var rows = jQuery("tr", table); // get all the rows as a wrapped set 151 rows.each(function() { 152 // Iterate through each row, the row is bound to "this" 153 var row = jQuery(this); 154 if (! row.hasClass("nodrag")) { 155 row.bind(startEvent, function(ev) { 156 if (ev.target.tagName == "TD") { 157 jQuery.tableDnD.initialiseDrag(this, table, this, ev, config); 158 return false; 159 } 160 }).css("cursor", "move"); // Store the tableDnD object 161 } 162 }); 163 } 164 }, 165 166 initialiseDrag: function(dragObject, table, target, evnt, config) { 167 jQuery.tableDnD.dragObject = dragObject; 168 jQuery.tableDnD.currentTable = table; 169 jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(target, evnt); 170 jQuery.tableDnD.originalOrder = jQuery.tableDnD.serialize(); 171 // Now we need to capture the mouse up and mouse move event 172 // We can use bind so that we don't interfere with other event handlers 173 jQuery(document) 174 .bind(moveEvent, jQuery.tableDnD.mousemove) 175 .bind(endEvent, jQuery.tableDnD.mouseup); 176 if (config.onDragStart) { 177 // Call the onDragStart method if there is one 178 config.onDragStart(table, target); 179 } 180 }, 181 182 updateTables: function() { 183 this.each(function() { 184 // this is now bound to each matching table 185 if (this.tableDnDConfig) { 186 jQuery.tableDnD.makeDraggable(this); 187 } 188 }) 189 }, 190 191 /** Get the mouse coordinates from the event (allowing for browser differences) */ 192 mouseCoords: function(ev){ 193 if(ev.pageX || ev.pageY){ 194 return {x:ev.pageX, y:ev.pageY}; 195 } 196 return { 197 x:ev.clientX + document.body.scrollLeft - document.body.clientLeft, 198 y:ev.clientY + document.body.scrollTop - document.body.clientTop 199 }; 200 }, 201 202 /** Given a target element and a mouse event, get the mouse offset from that element. 203 To do this we need the element's position and the mouse position */ 204 getMouseOffset: function(target, ev) { 205 ev = ev || window.event; 206 207 var docPos = this.getPosition(target); 208 var mousePos = this.mouseCoords(ev); 209 return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y}; 210 }, 211 212 /** Get the position of an element by going up the DOM tree and adding up all the offsets */ 213 getPosition: function(e){ 214 var left = 0; 215 var top = 0; 216 /** Safari fix -- thanks to Luis Chato for this! */ 217 if (e.offsetHeight == 0) { 218 /** Safari 2 doesn't correctly grab the offsetTop of a table row 219 this is detailed here: 220 http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/ 221 the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild. 222 note that firefox will return a text node as a first child, so designing a more thorough 223 solution may need to take that into account, for now this seems to work in firefox, safari, ie */ 224 e = e.firstChild; // a table cell 225 } 226 227 while (e.offsetParent){ 228 left += e.offsetLeft; 229 top += e.offsetTop; 230 e = e.offsetParent; 231 } 232 233 left += e.offsetLeft; 234 top += e.offsetTop; 235 236 return {x:left, y:top}; 237 }, 238 239 mousemove: function(ev) { 240 if (jQuery.tableDnD.dragObject == null) { 241 return; 242 } 243 if (ev.type == 'touchmove') { 244 // prevent touch device screen scrolling 245 event.preventDefault(); 246 } 247 248 var dragObj = jQuery(jQuery.tableDnD.dragObject); 249 var config = jQuery.tableDnD.currentTable.tableDnDConfig; 250 var mousePos = jQuery.tableDnD.mouseCoords(ev); 251 var y = mousePos.y - jQuery.tableDnD.mouseOffset.y; 252 //auto scroll the window 253 var yOffset = window.pageYOffset; 254 if (document.all) {
255 // Windows version 256 //yOffset=document.body.scrollTop; 257 if (typeof document.compatMode != 'undefined' && 258 document.compatMode != 'BackCompat') { 259 yOffset = document.documentElement.scrollTop; 260 } 261 else if (typeof document.body != 'undefined') { 262 yOffset=document.body.scrollTop; 263 } 264 } 265 266 if (mousePos.y-yOffset < config.scrollAmount) { 267 window.scrollBy(0, -config.scrollAmount); 268 } else { 269 var windowHeight = window.innerHeight ? window.innerHeight 270 : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight; 271 if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) { 272 window.scrollBy(0, config.scrollAmount); 273 } 274 } 275 276 if (y != jQuery.tableDnD.oldY) { 277 // work out if we're going up or down... 278 var movingDown = y > jQuery.tableDnD.oldY; 279 // update the old value 280 jQuery.tableDnD.oldY = y; 281 // update the style to show we're dragging 282 if (config.onDragClass) { 283 dragObj.addClass(config.onDragClass); 284 } else { 285 dragObj.css(config.onDragStyle); 286 } 287 // If we're over a row then move the dragged row to there so that the user sees the 288 // effect dynamically 289 var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y); 290 if (currentRow) { 291 // TODO worry about what happens when there are multiple TBODIES 292 if (movingDown && jQuery.tableDnD.dragObject != currentRow) { 293 jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling); 294 } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) { 295 jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow); 296 } 297 } 298 } 299 300 return false; 301 }, 302 303 /** We're only worried about the y position really, because we can only move rows up and down */ 304 findDropTargetRow: function(draggedRow, y) { 305 var rows = jQuery.tableDnD.currentTable.rows; 306 for (var i=0; i<rows.length; i++) { 307 var row = rows[i]; 308 var rowY = this.getPosition(row).y; 309 var rowHeight = parseInt(row.offsetHeight)/2; 310 if (row.offsetHeight == 0) { 311 rowY = this.getPosition(row.firstChild).y; 312 rowHeight = parseInt(row.firstChild.offsetHeight)/2; 313 } 314 // Because we always have to insert before, we need to offset the height a bit 315 if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) { 316 // that's the row we're over 317 // If it's the same as the current row, ignore it 318 if (row == draggedRow) {return null;} 319 var config = jQuery.tableDnD.currentTable.tableDnDConfig; 320 if (config.onAllowDrop) { 321 if (config.onAllowDrop(draggedRow, row)) { 322 return row; 323 } else { 324 return null; 325 } 326 } else { 327 // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic) 328 var nodrop = jQuery(row).hasClass("nodrop"); 329 if (! nodrop) { 330 return row; 331 } else { 332 return null; 333 } 334 } 335 return row; 336 } 337 } 338 return null; 339 }, 340 341 mouseup: function(e) { 342 if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) { 343 // Unbind the event handlers 344 jQuery(document) 345 .unbind(moveEvent, jQuery.tableDnD.mousemove) 346 .unbind(endEvent, jQuery.tableDnD.mouseup); 347 var droppedRow = jQuery.tableDnD.dragObject; 348 var config = jQuery.tableDnD.currentTable.tableDnDConfig; 349 // If we have a dragObject, then we need to release it, 350 // The row will already have been moved to the right place so we just reset stuff
351 if (config.onDragClass) { 352 jQuery(droppedRow).removeClass(config.onDragClass); 353 } else { 354 jQuery(droppedRow).css(config.onDropStyle); 355 } 356 jQuery.tableDnD.dragObject = null; 357 var newOrder = jQuery.tableDnD.serialize(); 358 if (config.onDrop && (jQuery.tableDnD.originalOrder != newOrder)) { 359 // Call the onDrop method if there is one 360 config.onDrop(jQuery.tableDnD.currentTable, droppedRow); 361 } 362 jQuery.tableDnD.currentTable = null; // let go of the table too 363 } 364 }, 365 366 serialize: function() { 367 if (jQuery.tableDnD.currentTable) { 368 return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable); 369 } else { 370 return "Error: No Table id set, you need to set an id on your table and every row"; 371 } 372 }, 373 374 serializeTable: function(table) { 375 var result = ""; 376 var tableId = table.id; 377 var rows = table.rows; 378 for (var i=0; i<rows.length; i++) { 379 if (result.length > 0) result += "&"; 380 var rowId = rows[i].id; 381 if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) { 382 rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0]; 383 } 384 385 result += tableId + '[]=' + rowId; 386 } 387 return result; 388 }, 389 390 serializeTables: function() { 391 var result = ""; 392 this.each(function() { 393 // this is now bound to each matching table 394 result += jQuery.tableDnD.serializeTable(this); 395 }); 396 return result; 397 } 398}; 399 400 401jQuery.fn.extend( 402 { 403 tableDnD : jQuery.tableDnD.build, 404 tableDnDUpdate : jQuery.tableDnD.updateTables, 405 tableDnDSerialize: jQuery.tableDnD.serializeTables 406 } 407);
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.