Skip to content
Browse files

added media for slickgrid

  • Loading branch information...
1 parent d136543 commit 2cdca717c0acc84b76cdec7ad8192b68b10a581c @sweemeng committed Nov 2, 2012
View
5 buildout.cfg
@@ -1,14 +1,19 @@
[buildout]
parts =
scrapedump
+ celery
develop = .
+[celery]
+recipe = collective.recipe.celery
+
[scrapedump]
recipe = zc.recipe.egg
interpreter = python
eggs = scrapedump
location = ${buildout:directory}/scrapedump
entry-points = scrapedump-run=webapp:run
+extra-paths = ${celery:scrapedump/celeryconfig.py}
[scrapedump-run]
recipe = zc.recipe.egg:scripts
View
157 scrapedump/static/css/slick.grid.css
@@ -0,0 +1,157 @@
+/*
+IMPORTANT:
+In order to preserve the uniform grid appearance, all cell styles need to have padding, margin and border sizes.
+No built-in (selected, editable, highlight, flashing, invalid, loading, :focus) or user-specified CSS
+classes should alter those!
+*/
+
+.slick-header.ui-state-default, .slick-headerrow.ui-state-default {
+ width: 100%;
+ overflow: hidden;
+ border-left: 0px;
+}
+
+.slick-header-columns, .slick-headerrow-columns {
+ position: relative;
+ white-space: nowrap;
+ cursor: default;
+ overflow: hidden;
+}
+
+.slick-header-column.ui-state-default {
+ position: relative;
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ height: 16px;
+ line-height: 16px;
+ margin: 0;
+ padding: 4px;
+ border-right: 1px solid silver;
+ border-left: 0px;
+ border-top: 0px;
+ border-bottom: 0px;
+ float: left;
+}
+
+.slick-headerrow-column.ui-state-default {
+ padding: 4px;
+}
+
+.slick-header-column-sorted {
+ font-style: italic;
+}
+
+.slick-sort-indicator {
+ display: inline-block;
+ width: 8px;
+ height: 5px;
+ margin-left: 4px;
+}
+
+.slick-sort-indicator-desc {
+ background: url(images/sort-desc.gif);
+}
+
+.slick-sort-indicator-asc {
+ background: url(images/sort-asc.gif);
+}
+
+.slick-resizable-handle {
+ position: absolute;
+ font-size: 0.1px;
+ display: block;
+ cursor: col-resize;
+ width: 4px;
+ right: 0px;
+ top: 0;
+ height: 100%;
+}
+
+.slick-sortable-placeholder {
+ background: silver;
+}
+
+.grid-canvas {
+ position: relative;
+ outline: 0;
+}
+
+.slick-row.ui-widget-content, .slick-row.ui-state-active {
+ position: absolute;
+ border: 0px;
+ width: 100%;
+}
+
+.slick-cell, .slick-headerrow-column {
+ position: absolute;
+
+ border: 1px solid transparent;
+ border-right: 1px dotted silver;
+ border-bottom-color: silver;
+
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ vertical-align: middle;
+ z-index: 1;
+ padding: 1px 2px 2px 1px;
+ margin: 0;
+
+ white-space: nowrap;
+
+ cursor: default;
+}
+
+.slick-group {
+}
+
+.slick-group-toggle {
+ display: inline-block;
+}
+
+.slick-cell.highlighted {
+ background: lightskyblue;
+ background: rgba(0, 0, 255, 0.2);
+ -webkit-transition: all 0.5s;
+ -moz-transition: all 0.5s;
+ transition: all 0.5s;
+}
+
+.slick-cell.flashing {
+ border: 1px solid red !important;
+}
+
+.slick-cell.editable {
+ z-index: 11;
+ overflow: visible;
+ background: white;
+ border-color: black;
+ border-style: solid;
+}
+
+.slick-cell:focus {
+ outline: none;
+}
+
+.slick-reorder-proxy {
+ display: inline-block;
+ background: blue;
+ opacity: 0.15;
+ filter: alpha(opacity = 15);
+ cursor: move;
+}
+
+.slick-reorder-guide {
+ display: inline-block;
+ height: 2px;
+ background: blue;
+ opacity: 0.7;
+ filter: alpha(opacity = 70);
+}
+
+.slick-selection {
+ z-index: 10;
+ position: absolute;
+ border: 2px dashed black;
+}
View
402 scrapedump/static/js/jquery.event.drag.js
@@ -0,0 +1,402 @@
+/*!
+ * jquery.event.drag - v 2.2
+ * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
+ * Open Source MIT License - http://threedubmedia.com/code/license
+ */
+// Created: 2008-06-04
+// Updated: 2012-05-21
+// REQUIRES: jquery 1.7.x
+
+;(function( $ ){
+
+// add the jquery instance method
+$.fn.drag = function( str, arg, opts ){
+ // figure out the event type
+ var type = typeof str == "string" ? str : "",
+ // figure out the event handler...
+ fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null;
+ // fix the event type
+ if ( type.indexOf("drag") !== 0 )
+ type = "drag"+ type;
+ // were options passed
+ opts = ( str == fn ? arg : opts ) || {};
+ // trigger or bind event handler
+ return fn ? this.bind( type, opts, fn ) : this.trigger( type );
+};
+
+// local refs (increase compression)
+var $event = $.event,
+$special = $event.special,
+// configure the drag special event
+drag = $special.drag = {
+
+ // these are the default settings
+ defaults: {
+ which: 1, // mouse button pressed to start drag sequence
+ distance: 0, // distance dragged before dragstart
+ not: ':input', // selector to suppress dragging on target elements
+ handle: null, // selector to match handle target elements
+ relative: false, // true to use "position", false to use "offset"
+ drop: true, // false to suppress drop events, true or selector to allow
+ click: false // false to suppress click events after dragend (no proxy)
+ },
+
+ // the key name for stored drag data
+ datakey: "dragdata",
+
+ // prevent bubbling for better performance
+ noBubble: true,
+
+ // count bound related events
+ add: function( obj ){
+ // read the interaction data
+ var data = $.data( this, drag.datakey ),
+ // read any passed options
+ opts = obj.data || {};
+ // count another realted event
+ data.related += 1;
+ // extend data options bound with this event
+ // don't iterate "opts" in case it is a node
+ $.each( drag.defaults, function( key, def ){
+ if ( opts[ key ] !== undefined )
+ data[ key ] = opts[ key ];
+ });
+ },
+
+ // forget unbound related events
+ remove: function(){
+ $.data( this, drag.datakey ).related -= 1;
+ },
+
+ // configure interaction, capture settings
+ setup: function(){
+ // check for related events
+ if ( $.data( this, drag.datakey ) )
+ return;
+ // initialize the drag data with copied defaults
+ var data = $.extend({ related:0 }, drag.defaults );
+ // store the interaction data
+ $.data( this, drag.datakey, data );
+ // bind the mousedown event, which starts drag interactions
+ $event.add( this, "touchstart mousedown", drag.init, data );
+ // prevent image dragging in IE...
+ if ( this.attachEvent )
+ this.attachEvent("ondragstart", drag.dontstart );
+ },
+
+ // destroy configured interaction
+ teardown: function(){
+ var data = $.data( this, drag.datakey ) || {};
+ // check for related events
+ if ( data.related )
+ return;
+ // remove the stored data
+ $.removeData( this, drag.datakey );
+ // remove the mousedown event
+ $event.remove( this, "touchstart mousedown", drag.init );
+ // enable text selection
+ drag.textselect( true );
+ // un-prevent image dragging in IE...
+ if ( this.detachEvent )
+ this.detachEvent("ondragstart", drag.dontstart );
+ },
+
+ // initialize the interaction
+ init: function( event ){
+ // sorry, only one touch at a time
+ if ( drag.touched )
+ return;
+ // the drag/drop interaction data
+ var dd = event.data, results;
+ // check the which directive
+ if ( event.which != 0 && dd.which > 0 && event.which != dd.which )
+ return;
+ // check for suppressed selector
+ if ( $( event.target ).is( dd.not ) )
+ return;
+ // check for handle selector
+ if ( dd.handle && !$( event.target ).closest( dd.handle, event.currentTarget ).length )
+ return;
+
+ drag.touched = event.type == 'touchstart' ? this : null;
+ dd.propagates = 1;
+ dd.mousedown = this;
+ dd.interactions = [ drag.interaction( this, dd ) ];
+ dd.target = event.target;
+ dd.pageX = event.pageX;
+ dd.pageY = event.pageY;
+ dd.dragging = null;
+ // handle draginit event...
+ results = drag.hijack( event, "draginit", dd );
+ // early cancel
+ if ( !dd.propagates )
+ return;
+ // flatten the result set
+ results = drag.flatten( results );
+ // insert new interaction elements
+ if ( results && results.length ){
+ dd.interactions = [];
+ $.each( results, function(){
+ dd.interactions.push( drag.interaction( this, dd ) );
+ });
+ }
+ // remember how many interactions are propagating
+ dd.propagates = dd.interactions.length;
+ // locate and init the drop targets
+ if ( dd.drop !== false && $special.drop )
+ $special.drop.handler( event, dd );
+ // disable text selection
+ drag.textselect( false );
+ // bind additional events...
+ if ( drag.touched )
+ $event.add( drag.touched, "touchmove touchend", drag.handler, dd );
+ else
+ $event.add( document, "mousemove mouseup", drag.handler, dd );
+ // helps prevent text selection or scrolling
+ if ( !drag.touched || dd.live )
+ return false;
+ },
+
+ // returns an interaction object
+ interaction: function( elem, dd ){
+ var offset = $( elem )[ dd.relative ? "position" : "offset" ]() || { top:0, left:0 };
+ return {
+ drag: elem,
+ callback: new drag.callback(),
+ droppable: [],
+ offset: offset
+ };
+ },
+
+ // handle drag-releatd DOM events
+ handler: function( event ){
+ // read the data before hijacking anything
+ var dd = event.data;
+ // handle various events
+ switch ( event.type ){
+ // mousemove, check distance, start dragging
+ case !dd.dragging && 'touchmove':
+ event.preventDefault();
+ case !dd.dragging && 'mousemove':
+ // drag tolerance, x² + y² = distance²
+ if ( Math.pow( event.pageX-dd.pageX, 2 ) + Math.pow( event.pageY-dd.pageY, 2 ) < Math.pow( dd.distance, 2 ) )
+ break; // distance tolerance not reached
+ event.target = dd.target; // force target from "mousedown" event (fix distance issue)
+ drag.hijack( event, "dragstart", dd ); // trigger "dragstart"
+ if ( dd.propagates ) // "dragstart" not rejected
+ dd.dragging = true; // activate interaction
+ // mousemove, dragging
+ case 'touchmove':
+ event.preventDefault();
+ case 'mousemove':
+ if ( dd.dragging ){
+ // trigger "drag"
+ drag.hijack( event, "drag", dd );
+ if ( dd.propagates ){
+ // manage drop events
+ if ( dd.drop !== false && $special.drop )
+ $special.drop.handler( event, dd ); // "dropstart", "dropend"
+ break; // "drag" not rejected, stop
+ }
+ event.type = "mouseup"; // helps "drop" handler behave
+ }
+ // mouseup, stop dragging
+ case 'touchend':
+ case 'mouseup':
+ default:
+ if ( drag.touched )
+ $event.remove( drag.touched, "touchmove touchend", drag.handler ); // remove touch events
+ else
+ $event.remove( document, "mousemove mouseup", drag.handler ); // remove page events
+ if ( dd.dragging ){
+ if ( dd.drop !== false && $special.drop )
+ $special.drop.handler( event, dd ); // "drop"
+ drag.hijack( event, "dragend", dd ); // trigger "dragend"
+ }
+ drag.textselect( true ); // enable text selection
+ // if suppressing click events...
+ if ( dd.click === false && dd.dragging )
+ $.data( dd.mousedown, "suppress.click", new Date().getTime() + 5 );
+ dd.dragging = drag.touched = false; // deactivate element
+ break;
+ }
+ },
+
+ // re-use event object for custom events
+ hijack: function( event, type, dd, x, elem ){
+ // not configured
+ if ( !dd )
+ return;
+ // remember the original event and type
+ var orig = { event:event.originalEvent, type:event.type },
+ // is the event drag related or drog related?
+ mode = type.indexOf("drop") ? "drag" : "drop",
+ // iteration vars
+ result, i = x || 0, ia, $elems, callback,
+ len = !isNaN( x ) ? x : dd.interactions.length;
+ // modify the event type
+ event.type = type;
+ // remove the original event
+ event.originalEvent = null;
+ // initialize the results
+ dd.results = [];
+ // handle each interacted element
+ do if ( ia = dd.interactions[ i ] ){
+ // validate the interaction
+ if ( type !== "dragend" && ia.cancelled )
+ continue;
+ // set the dragdrop properties on the event object
+ callback = drag.properties( event, dd, ia );
+ // prepare for more results
+ ia.results = [];
+ // handle each element
+ $( elem || ia[ mode ] || dd.droppable ).each(function( p, subject ){
+ // identify drag or drop targets individually
+ callback.target = subject;
+ // force propagtion of the custom event
+ event.isPropagationStopped = function(){ return false; };
+ // handle the event
+ result = subject ? $event.dispatch.call( subject, event, callback ) : null;
+ // stop the drag interaction for this element
+ if ( result === false ){
+ if ( mode == "drag" ){
+ ia.cancelled = true;
+ dd.propagates -= 1;
+ }
+ if ( type == "drop" ){
+ ia[ mode ][p] = null;
+ }
+ }
+ // assign any dropinit elements
+ else if ( type == "dropinit" )
+ ia.droppable.push( drag.element( result ) || subject );
+ // accept a returned proxy element
+ if ( type == "dragstart" )
+ ia.proxy = $( drag.element( result ) || ia.drag )[0];
+ // remember this result
+ ia.results.push( result );
+ // forget the event result, for recycling
+ delete event.result;
+ // break on cancelled handler
+ if ( type !== "dropinit" )
+ return result;
+ });
+ // flatten the results
+ dd.results[ i ] = drag.flatten( ia.results );
+ // accept a set of valid drop targets
+ if ( type == "dropinit" )
+ ia.droppable = drag.flatten( ia.droppable );
+ // locate drop targets
+ if ( type == "dragstart" && !ia.cancelled )
+ callback.update();
+ }
+ while ( ++i < len )
+ // restore the original event & type
+ event.type = orig.type;
+ event.originalEvent = orig.event;
+ // return all handler results
+ return drag.flatten( dd.results );
+ },
+
+ // extend the callback object with drag/drop properties...
+ properties: function( event, dd, ia ){
+ var obj = ia.callback;
+ // elements
+ obj.drag = ia.drag;
+ obj.proxy = ia.proxy || ia.drag;
+ // starting mouse position
+ obj.startX = dd.pageX;
+ obj.startY = dd.pageY;
+ // current distance dragged
+ obj.deltaX = event.pageX - dd.pageX;
+ obj.deltaY = event.pageY - dd.pageY;
+ // original element position
+ obj.originalX = ia.offset.left;
+ obj.originalY = ia.offset.top;
+ // adjusted element position
+ obj.offsetX = obj.originalX + obj.deltaX;
+ obj.offsetY = obj.originalY + obj.deltaY;
+ // assign the drop targets information
+ obj.drop = drag.flatten( ( ia.drop || [] ).slice() );
+ obj.available = drag.flatten( ( ia.droppable || [] ).slice() );
+ return obj;
+ },
+
+ // determine is the argument is an element or jquery instance
+ element: function( arg ){
+ if ( arg && ( arg.jquery || arg.nodeType == 1 ) )
+ return arg;
+ },
+
+ // flatten nested jquery objects and arrays into a single dimension array
+ flatten: function( arr ){
+ return $.map( arr, function( member ){
+ return member && member.jquery ? $.makeArray( member ) :
+ member && member.length ? drag.flatten( member ) : member;
+ });
+ },
+
+ // toggles text selection attributes ON (true) or OFF (false)
+ textselect: function( bool ){
+ $( document )[ bool ? "unbind" : "bind" ]("selectstart", drag.dontstart )
+ .css("MozUserSelect", bool ? "" : "none" );
+ // .attr("unselectable", bool ? "off" : "on" )
+ document.unselectable = bool ? "off" : "on";
+ },
+
+ // suppress "selectstart" and "ondragstart" events
+ dontstart: function(){
+ return false;
+ },
+
+ // a callback instance contructor
+ callback: function(){}
+
+};
+
+// callback methods
+drag.callback.prototype = {
+ update: function(){
+ if ( $special.drop && this.available.length )
+ $.each( this.available, function( i ){
+ $special.drop.locate( this, i );
+ });
+ }
+};
+
+// patch $.event.$dispatch to allow suppressing clicks
+var $dispatch = $event.dispatch;
+$event.dispatch = function( event ){
+ if ( $.data( this, "suppress."+ event.type ) - new Date().getTime() > 0 ){
+ $.removeData( this, "suppress."+ event.type );
+ return;
+ }
+ return $dispatch.apply( this, arguments );
+};
+
+// event fix hooks for touch events...
+var touchHooks =
+$event.fixHooks.touchstart =
+$event.fixHooks.touchmove =
+$event.fixHooks.touchend =
+$event.fixHooks.touchcancel = {
+ props: "clientX clientY pageX pageY screenX screenY".split( " " ),
+ filter: function( event, orig ) {
+ if ( orig ){
+ var touched = ( orig.touches && orig.touches[0] )
+ || ( orig.changedTouches && orig.changedTouches[0] )
+ || null;
+ // iOS webkit: touchstart, touchmove, touchend
+ if ( touched )
+ $.each( touchHooks.props, function( i, prop ){
+ event[ prop ] = touched[ prop ];
+ });
+ }
+ return event;
+ }
+};
+
+// share the same special event configuration with related events...
+$special.draginit = $special.dragstart = $special.dragend = drag;
+
+})( jQuery );
View
87 scrapedump/static/js/jquery.event.drag.live-2.2.js
@@ -0,0 +1,87 @@
+/*!
+ * jquery.event.drag.live - v 2.2
+ * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
+ * Open Source MIT License - http://threedubmedia.com/code/license
+ */
+// Created: 2010-06-07
+// Updated: 2012-05-21
+// REQUIRES: jquery 1.7.x, event.drag 2.2
+
+;(function( $ ){
+
+// local refs (increase compression)
+var $event = $.event,
+// ref the special event config
+drag = $event.special.drag,
+// old drag event add method
+origadd = drag.add,
+// old drag event teradown method
+origteardown = drag.teardown;
+
+// allow events to bubble for delegation
+drag.noBubble = false;
+
+// the namespace for internal live events
+drag.livekey = "livedrag";
+
+// new drop event add method
+drag.add = function( obj ){
+ // call the old method
+ origadd.apply( this, arguments );
+ // read the data
+ var data = $.data( this, drag.datakey );
+ // bind the live "draginit" delegator
+ if ( !data.live && obj.selector ){
+ data.live = true;
+ $event.add( this, "draginit."+ drag.livekey, drag.delegate );
+ }
+};
+
+// new drop event teardown method
+drag.teardown = function(){
+ // call the old method
+ origteardown.apply( this, arguments );
+ // read the data
+ var data = $.data( this, drag.datakey ) || {};
+ // bind the live "draginit" delegator
+ if ( data.live ){
+ // remove the "live" delegation
+ $event.remove( this, "draginit."+ drag.livekey, drag.delegate );
+ data.live = false;
+ }
+};
+
+// identify potential delegate elements
+drag.delegate = function( event ){
+ // local refs
+ var elems = [], target,
+ // element event structure
+ events = $.data( this, "events" ) || {};
+ // query live events
+ $.each( events || [], function( key, arr ){
+ // no event type matches
+ if ( key.indexOf("drag") !== 0 )
+ return;
+ $.each( arr || [], function( i, obj ){
+ // locate the element to delegate
+ target = $( event.target ).closest( obj.selector, event.currentTarget )[0];
+ // no element found
+ if ( !target )
+ return;
+ // add an event handler
+ $event.add( target, obj.origType+'.'+drag.livekey, obj.origHandler || obj.handler, obj.data );
+ // remember new elements
+ if ( $.inArray( target, elems ) < 0 )
+ elems.push( target );
+ });
+ });
+ // if there are no elements, break
+ if ( !elems.length )
+ return false;
+ // return the matched results, and clenup when complete
+ return $( elems ).bind("dragend."+ drag.livekey, function(){
+ $event.remove( this, "."+ drag.livekey ); // cleanup delegation
+ });
+};
+
+})( jQuery );
View
48 scrapedump/static/js/plugins/slick.autotooltips.js
@@ -0,0 +1,48 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "AutoTooltips": AutoTooltips
+ }
+ });
+
+
+ function AutoTooltips(options) {
+ var _grid;
+ var _self = this;
+ var _defaults = {
+ maxToolTipLength: null
+ };
+
+ function init(grid) {
+ options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ _grid.onMouseEnter.subscribe(handleMouseEnter);
+ }
+
+ function destroy() {
+ _grid.onMouseEnter.unsubscribe(handleMouseEnter);
+ }
+
+ function handleMouseEnter(e, args) {
+ var cell = _grid.getCellFromEvent(e);
+ if (cell) {
+ var node = _grid.getCellNode(cell.row, cell.cell);
+ if ($(node).innerWidth() < node.scrollWidth) {
+ var text = $.trim($(node).text());
+ if (options.maxToolTipLength && text.length > options.maxToolTipLength) {
+ text = text.substr(0, options.maxToolTipLength - 3) + "...";
+ }
+ $(node).attr("title", text);
+ } else {
+ $(node).attr("title", "");
+ }
+ }
+ }
+
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy
+ });
+ }
+})(jQuery);
View
86 scrapedump/static/js/plugins/slick.cellcopymanager.js
@@ -0,0 +1,86 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "CellCopyManager": CellCopyManager
+ }
+ });
+
+
+ function CellCopyManager() {
+ var _grid;
+ var _self = this;
+ var _copiedRanges;
+
+ function init(grid) {
+ _grid = grid;
+ _grid.onKeyDown.subscribe(handleKeyDown);
+ }
+
+ function destroy() {
+ _grid.onKeyDown.unsubscribe(handleKeyDown);
+ }
+
+ function handleKeyDown(e, args) {
+ var ranges;
+ if (!_grid.getEditorLock().isActive()) {
+ if (e.which == $.ui.keyCode.ESCAPE) {
+ if (_copiedRanges) {
+ e.preventDefault();
+ clearCopySelection();
+ _self.onCopyCancelled.notify({ranges: _copiedRanges});
+ _copiedRanges = null;
+ }
+ }
+
+ if (e.which == 67 && (e.ctrlKey || e.metaKey)) {
+ ranges = _grid.getSelectionModel().getSelectedRanges();
+ if (ranges.length != 0) {
+ e.preventDefault();
+ _copiedRanges = ranges;
+ markCopySelection(ranges);
+ _self.onCopyCells.notify({ranges: ranges});
+ }
+ }
+
+ if (e.which == 86 && (e.ctrlKey || e.metaKey)) {
+ if (_copiedRanges) {
+ e.preventDefault();
+ clearCopySelection();
+ ranges = _grid.getSelectionModel().getSelectedRanges();
+ _self.onPasteCells.notify({from: _copiedRanges, to: ranges});
+ _copiedRanges = null;
+ }
+ }
+ }
+ }
+
+ function markCopySelection(ranges) {
+ var columns = _grid.getColumns();
+ var hash = {};
+ for (var i = 0; i < ranges.length; i++) {
+ for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
+ hash[j] = {};
+ for (var k = ranges[i].fromCell; k <= ranges[i].toCell; k++) {
+ hash[j][columns[k].id] = "copied";
+ }
+ }
+ }
+ _grid.setCellCssStyles("copy-manager", hash);
+ }
+
+ function clearCopySelection() {
+ _grid.removeCellCssStyles("copy-manager");
+ }
+
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy,
+ "clearCopySelection": clearCopySelection,
+
+ "onCopyCells": new Slick.Event(),
+ "onCopyCancelled": new Slick.Event(),
+ "onPasteCells": new Slick.Event()
+ });
+ }
+})(jQuery);
View
64 scrapedump/static/js/plugins/slick.cellrangedecorator.js
@@ -0,0 +1,64 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "CellRangeDecorator": CellRangeDecorator
+ }
+ });
+
+ /***
+ * Displays an overlay on top of a given cell range.
+ *
+ * TODO:
+ * Currently, it blocks mouse events to DOM nodes behind it.
+ * Use FF and WebKit-specific "pointer-events" CSS style, or some kind of event forwarding.
+ * Could also construct the borders separately using 4 individual DIVs.
+ *
+ * @param {Grid} grid
+ * @param {Object} options
+ */
+ function CellRangeDecorator(grid, options) {
+ var _elem;
+ var _defaults = {
+ selectionCss: {
+ "zIndex": "9999",
+ "border": "2px dashed red"
+ }
+ };
+
+ options = $.extend(true, {}, _defaults, options);
+
+
+ function show(range) {
+ if (!_elem) {
+ _elem = $("<div></div>", {css: options.selectionCss})
+ .css("position", "absolute")
+ .appendTo(grid.getCanvasNode());
+ }
+
+ var from = grid.getCellNodeBox(range.fromRow, range.fromCell);
+ var to = grid.getCellNodeBox(range.toRow, range.toCell);
+
+ _elem.css({
+ top: from.top - 1,
+ left: from.left - 1,
+ height: to.bottom - from.top - 2,
+ width: to.right - from.left - 2
+ });
+
+ return _elem;
+ }
+
+ function hide() {
+ if (_elem) {
+ _elem.remove();
+ _elem = null;
+ }
+ }
+
+ $.extend(this, {
+ "show": show,
+ "hide": hide
+ });
+ }
+})(jQuery);
View
111 scrapedump/static/js/plugins/slick.cellrangeselector.js
@@ -0,0 +1,111 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "CellRangeSelector": CellRangeSelector
+ }
+ });
+
+
+ function CellRangeSelector(options) {
+ var _grid;
+ var _canvas;
+ var _dragging;
+ var _decorator;
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _defaults = {
+ selectionCss: {
+ "border": "2px dashed blue"
+ }
+ };
+
+
+ function init(grid) {
+ options = $.extend(true, {}, _defaults, options);
+ _decorator = new Slick.CellRangeDecorator(grid, options);
+ _grid = grid;
+ _canvas = _grid.getCanvasNode();
+ _handler
+ .subscribe(_grid.onDragInit, handleDragInit)
+ .subscribe(_grid.onDragStart, handleDragStart)
+ .subscribe(_grid.onDrag, handleDrag)
+ .subscribe(_grid.onDragEnd, handleDragEnd);
+ }
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ }
+
+ function handleDragInit(e, dd) {
+ // prevent the grid from cancelling drag'n'drop by default
+ e.stopImmediatePropagation();
+ }
+
+ function handleDragStart(e, dd) {
+ var cell = _grid.getCellFromEvent(e);
+ if (_self.onBeforeCellRangeSelected.notify(cell) !== false) {
+ if (_grid.canCellBeSelected(cell.row, cell.cell)) {
+ _dragging = true;
+ e.stopImmediatePropagation();
+ }
+ }
+ if (!_dragging) {
+ return;
+ }
+
+ var start = _grid.getCellFromPoint(
+ dd.startX - $(_canvas).offset().left,
+ dd.startY - $(_canvas).offset().top);
+
+ dd.range = {start: start, end: {}};
+
+ return _decorator.show(new Slick.Range(start.row, start.cell));
+ }
+
+ function handleDrag(e, dd) {
+ if (!_dragging) {
+ return;
+ }
+ e.stopImmediatePropagation();
+
+ var end = _grid.getCellFromPoint(
+ e.pageX - $(_canvas).offset().left,
+ e.pageY - $(_canvas).offset().top);
+
+ if (!_grid.canCellBeSelected(end.row, end.cell)) {
+ return;
+ }
+
+ dd.range.end = end;
+ _decorator.show(new Slick.Range(dd.range.start.row, dd.range.start.cell, end.row, end.cell));
+ }
+
+ function handleDragEnd(e, dd) {
+ if (!_dragging) {
+ return;
+ }
+
+ _dragging = false;
+ e.stopImmediatePropagation();
+
+ _decorator.hide();
+ _self.onCellRangeSelected.notify({
+ range: new Slick.Range(
+ dd.range.start.row,
+ dd.range.start.cell,
+ dd.range.end.row,
+ dd.range.end.cell
+ )
+ });
+ }
+
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy,
+
+ "onBeforeCellRangeSelected": new Slick.Event(),
+ "onCellRangeSelected": new Slick.Event()
+ });
+ }
+})(jQuery);
View
92 scrapedump/static/js/plugins/slick.cellselectionmodel.js
@@ -0,0 +1,92 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "CellSelectionModel": CellSelectionModel
+ }
+ });
+
+
+ function CellSelectionModel(options) {
+ var _grid;
+ var _canvas;
+ var _ranges = [];
+ var _self = this;
+ var _selector = new Slick.CellRangeSelector({
+ "selectionCss": {
+ "border": "2px solid black"
+ }
+ });
+ var _options;
+ var _defaults = {
+ selectActiveCell: true
+ };
+
+
+ function init(grid) {
+ _options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ _canvas = _grid.getCanvasNode();
+ _grid.onActiveCellChanged.subscribe(handleActiveCellChange);
+ grid.registerPlugin(_selector);
+ _selector.onCellRangeSelected.subscribe(handleCellRangeSelected);
+ _selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected);
+ }
+
+ function destroy() {
+ _grid.onActiveCellChanged.unsubscribe(handleActiveCellChange);
+ _selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected);
+ _selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected);
+ _grid.unregisterPlugin(_selector);
+ }
+
+ function removeInvalidRanges(ranges) {
+ var result = [];
+
+ for (var i = 0; i < ranges.length; i++) {
+ var r = ranges[i];
+ if (_grid.canCellBeSelected(r.fromRow, r.fromCell) && _grid.canCellBeSelected(r.toRow, r.toCell)) {
+ result.push(r);
+ }
+ }
+
+ return result;
+ }
+
+ function setSelectedRanges(ranges) {
+ _ranges = removeInvalidRanges(ranges);
+ _self.onSelectedRangesChanged.notify(_ranges);
+ }
+
+ function getSelectedRanges() {
+ return _ranges;
+ }
+
+ function handleBeforeCellRangeSelected(e, args) {
+ if (_grid.getEditorLock().isActive()) {
+ e.stopPropagation();
+ return false;
+ }
+ }
+
+ function handleCellRangeSelected(e, args) {
+ setSelectedRanges([args.range]);
+ }
+
+ function handleActiveCellChange(e, args) {
+ if (_options.selectActiveCell) {
+ setSelectedRanges([new Slick.Range(args.row, args.cell)]);
+ }
+ }
+
+ $.extend(this, {
+ "getSelectedRanges": getSelectedRanges,
+ "setSelectedRanges": setSelectedRanges,
+
+ "init": init,
+ "destroy": destroy,
+
+ "onSelectedRangesChanged": new Slick.Event()
+ });
+ }
+})(jQuery);
View
153 scrapedump/static/js/plugins/slick.checkboxselectcolumn.js
@@ -0,0 +1,153 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "CheckboxSelectColumn": CheckboxSelectColumn
+ }
+ });
+
+
+ function CheckboxSelectColumn(options) {
+ var _grid;
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _selectedRowsLookup = {};
+ var _defaults = {
+ columnId: "_checkbox_selector",
+ cssClass: null,
+ toolTip: "Select/Deselect All",
+ width: 30
+ };
+
+ var _options = $.extend(true, {}, _defaults, options);
+
+ function init(grid) {
+ _grid = grid;
+ _handler
+ .subscribe(_grid.onSelectedRowsChanged, handleSelectedRowsChanged)
+ .subscribe(_grid.onClick, handleClick)
+ .subscribe(_grid.onHeaderClick, handleHeaderClick)
+ .subscribe(_grid.onKeyDown, handleKeyDown);
+ }
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ }
+
+ function handleSelectedRowsChanged(e, args) {
+ var selectedRows = _grid.getSelectedRows();
+ var lookup = {}, row, i;
+ for (i = 0; i < selectedRows.length; i++) {
+ row = selectedRows[i];
+ lookup[row] = true;
+ if (lookup[row] !== _selectedRowsLookup[row]) {
+ _grid.invalidateRow(row);
+ delete _selectedRowsLookup[row];
+ }
+ }
+ for (i in _selectedRowsLookup) {
+ _grid.invalidateRow(i);
+ }
+ _selectedRowsLookup = lookup;
+ _grid.render();
+
+ if (selectedRows.length && selectedRows.length == _grid.getDataLength()) {
+ _grid.updateColumnHeader(_options.columnId, "<input type='checkbox' checked='checked'>", _options.toolTip);
+ } else {
+ _grid.updateColumnHeader(_options.columnId, "<input type='checkbox'>", _options.toolTip);
+ }
+ }
+
+ function handleKeyDown(e, args) {
+ if (e.which == 32) {
+ if (_grid.getColumns()[args.cell].id === _options.columnId) {
+ // if editing, try to commit
+ if (!_grid.getEditorLock().isActive() || _grid.getEditorLock().commitCurrentEdit()) {
+ toggleRowSelection(args.row);
+ }
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ }
+ }
+ }
+
+ function handleClick(e, args) {
+ // clicking on a row select checkbox
+ if (_grid.getColumns()[args.cell].id === _options.columnId && $(e.target).is(":checkbox")) {
+ // if editing, try to commit
+ if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ return;
+ }
+
+ toggleRowSelection(args.row);
+ e.stopPropagation();
+ e.stopImmediatePropagation();
+ }
+ }
+
+ function toggleRowSelection(row) {
+ if (_selectedRowsLookup[row]) {
+ _grid.setSelectedRows($.grep(_grid.getSelectedRows(), function (n) {
+ return n != row
+ }));
+ } else {
+ _grid.setSelectedRows(_grid.getSelectedRows().concat(row));
+ }
+ }
+
+ function handleHeaderClick(e, args) {
+ if (args.column.id == _options.columnId && $(e.target).is(":checkbox")) {
+ // if editing, try to commit
+ if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ return;
+ }
+
+ if ($(e.target).is(":checked")) {
+ var rows = [];
+ for (var i = 0; i < _grid.getDataLength(); i++) {
+ rows.push(i);
+ }
+ _grid.setSelectedRows(rows);
+ } else {
+ _grid.setSelectedRows([]);
+ }
+ e.stopPropagation();
+ e.stopImmediatePropagation();
+ }
+ }
+
+ function getColumnDefinition() {
+ return {
+ id: _options.columnId,
+ name: "<input type='checkbox'>",
+ toolTip: _options.toolTip,
+ field: "sel",
+ width: _options.width,
+ resizable: false,
+ sortable: false,
+ cssClass: _options.cssClass,
+ formatter: checkboxSelectionFormatter
+ };
+ }
+
+ function checkboxSelectionFormatter(row, cell, value, columnDef, dataContext) {
+ if (dataContext) {
+ return _selectedRowsLookup[row]
+ ? "<input type='checkbox' checked='checked'>"
+ : "<input type='checkbox'>";
+ }
+ return null;
+ }
+
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy,
+
+ "getColumnDefinition": getColumnDefinition
+ });
+ }
+})(jQuery);
View
38 scrapedump/static/js/plugins/slick.headerbuttons.css
@@ -0,0 +1,38 @@
+.slick-column-name {
+ /**
+ * This makes all "float:right" elements after it that spill over to the next line
+ * display way below the lower boundary of the column thus hiding them.
+ */
+ display: inline-block;
+ float: left;
+ margin-bottom: 100px;
+}
+
+.slick-header-button {
+ display: inline-block;
+ float: right;
+ vertical-align: top;
+ margin: 1px;
+ /**
+ * This makes all "float:right" elements after it that spill over to the next line
+ * display way below the lower boundary of the column thus hiding them.
+ */
+ margin-bottom: 100px;
+ height: 15px;
+ width: 15px;
+ background-repeat: no-repeat;
+ background-position: center center;
+ cursor: pointer;
+}
+
+.slick-header-button-hidden {
+ width: 0;
+
+ -webkit-transition: 0.2s width;
+ -ms-transition: 0.2s width;
+ transition: 0.2s width;
+}
+
+.slick-header-column:hover > .slick-header-button {
+ width: 15px;
+}
View
177 scrapedump/static/js/plugins/slick.headerbuttons.js
@@ -0,0 +1,177 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "Plugins": {
+ "HeaderButtons": HeaderButtons
+ }
+ }
+ });
+
+
+ /***
+ * A plugin to add custom buttons to column headers.
+ *
+ * USAGE:
+ *
+ * Add the plugin .js & .css files and register it with the grid.
+ *
+ * To specify a custom button in a column header, extend the column definition like so:
+ *
+ * var columns = [
+ * {
+ * id: 'myColumn',
+ * name: 'My column',
+ *
+ * // This is the relevant part
+ * header: {
+ * buttons: [
+ * {
+ * // button options
+ * },
+ * {
+ * // button options
+ * }
+ * ]
+ * }
+ * }
+ * ];
+ *
+ * Available button options:
+ * cssClass: CSS class to add to the button.
+ * image: Relative button image path.
+ * tooltip: Button tooltip.
+ * showOnHover: Only show the button on hover.
+ * handler: Button click handler.
+ * command: A command identifier to be passed to the onCommand event handlers.
+ *
+ * The plugin exposes the following events:
+ * onCommand: Fired on button click for buttons with 'command' specified.
+ * Event args:
+ * grid: Reference to the grid.
+ * column: Column definition.
+ * command: Button command identified.
+ * button: Button options. Note that you can change the button options in your
+ * event handler, and the column header will be automatically updated to
+ * reflect them. This is useful if you want to implement something like a
+ * toggle button.
+ *
+ *
+ * @param options {Object} Options:
+ * buttonCssClass: a CSS class to use for buttons (default 'slick-header-button')
+ * @class Slick.Plugins.HeaderButtons
+ * @constructor
+ */
+ function HeaderButtons(options) {
+ var _grid;
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _defaults = {
+ buttonCssClass: "slick-header-button"
+ };
+
+
+ function init(grid) {
+ options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ _handler
+ .subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered)
+ .subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy);
+
+ // Force the grid to re-render the header now that the events are hooked up.
+ _grid.setColumns(_grid.getColumns());
+ }
+
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ }
+
+
+ function handleHeaderCellRendered(e, args) {
+ var column = args.column;
+
+ if (column.header && column.header.buttons) {
+ // Append buttons in reverse order since they are floated to the right.
+ var i = column.header.buttons.length;
+ while (i--) {
+ var button = column.header.buttons[i];
+ var btn = $("<div></div>")
+ .addClass(options.buttonCssClass)
+ .data("column", column)
+ .data("button", button);
+
+ if (button.showOnHover) {
+ btn.addClass("slick-header-button-hidden");
+ }
+
+ if (button.image) {
+ btn.css("backgroundImage", "url(" + button.image + ")");
+ }
+
+ if (button.cssClass) {
+ btn.addClass(button.cssClass);
+ }
+
+ if (button.tooltip) {
+ btn.attr("title", button.tooltip);
+ }
+
+ if (button.command) {
+ btn.data("command", button.command);
+ }
+
+ if (button.handler) {
+ btn.bind("click", button.handler);
+ }
+
+ btn
+ .bind("click", handleButtonClick)
+ .appendTo(args.node);
+ }
+ }
+ }
+
+
+ function handleBeforeHeaderCellDestroy(e, args) {
+ var column = args.column;
+
+ if (column.header && column.header.buttons) {
+ // Removing buttons via jQuery will also clean up any event handlers and data.
+ // NOTE: If you attach event handlers directly or using a different framework,
+ // you must also clean them up here to avoid memory leaks.
+ $(args.node).find("." + options.buttonCssClass).remove();
+ }
+ }
+
+
+ function handleButtonClick(e) {
+ var command = $(this).data("command");
+ var columnDef = $(this).data("column");
+ var button = $(this).data("button");
+
+ if (command != null) {
+ _self.onCommand.notify({
+ "grid": _grid,
+ "column": columnDef,
+ "command": command,
+ "button": button
+ }, e, _self);
+
+ // Update the header in case the user updated the button definition in the handler.
+ _grid.updateColumnHeader(columnDef.id);
+ }
+
+ // Stop propagation so that it doesn't register as a header click event.
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy,
+
+ "onCommand": new Slick.Event()
+ });
+ }
+})(jQuery);
View
58 scrapedump/static/js/plugins/slick.headermenu.css
@@ -0,0 +1,58 @@
+/* Menu button */
+.slick-header-menubutton {
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ width: 14px;
+ background-repeat: no-repeat;
+ background-position: left center;
+ cursor: pointer;
+
+ display: none;
+ border-left: thin ridge silver;
+}
+
+.slick-header-column:hover > .slick-header-menubutton,
+.slick-header-column-active .slick-header-menubutton {
+ display: inline-block;
+}
+
+/* Menu */
+.slick-header-menu {
+ position: absolute;
+ display: inline-block;
+ margin: 0;
+ padding: 2px;
+ cursor: default;
+}
+
+
+/* Menu items */
+.slick-header-menuitem {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ cursor: pointer;
+}
+
+.slick-header-menuicon {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ vertical-align: middle;
+ margin-right: 4px;
+ background-repeat: no-repeat;
+ background-position: center center;
+}
+
+.slick-header-menucontent {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+
+/* Disabled */
+.slick-header-menuitem-disabled {
+ color: silver;
+}
View
272 scrapedump/static/js/plugins/slick.headermenu.js
@@ -0,0 +1,272 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "Plugins": {
+ "HeaderMenu": HeaderMenu
+ }
+ }
+ });
+
+
+ /***
+ * A plugin to add drop-down menus to column headers.
+ *
+ * USAGE:
+ *
+ * Add the plugin .js & .css files and register it with the grid.
+ *
+ * To specify a menu in a column header, extend the column definition like so:
+ *
+ * var columns = [
+ * {
+ * id: 'myColumn',
+ * name: 'My column',
+ *
+ * // This is the relevant part
+ * header: {
+ * menu: {
+ * items: [
+ * {
+ * // menu item options
+ * },
+ * {
+ * // menu item options
+ * }
+ * ]
+ * }
+ * }
+ * }
+ * ];
+ *
+ *
+ * Available menu options:
+ * tooltip: Menu button tooltip.
+ *
+ *
+ * Available menu item options:
+ * title: Menu item text.
+ * disabled: Whether the item is disabled.
+ * tooltip: Item tooltip.
+ * command: A command identifier to be passed to the onCommand event handlers.
+ * iconCssClass: A CSS class to be added to the menu item icon.
+ * iconImage: A url to the icon image.
+ *
+ *
+ * The plugin exposes the following events:
+ * onBeforeMenuShow: Fired before the menu is shown. You can customize the menu or dismiss it by returning false.
+ * Event args:
+ * grid: Reference to the grid.
+ * column: Column definition.
+ * menu: Menu options. Note that you can change the menu items here.
+ *
+ * onCommand: Fired on menu item click for buttons with 'command' specified.
+ * Event args:
+ * grid: Reference to the grid.
+ * column: Column definition.
+ * command: Button command identified.
+ * button: Button options. Note that you can change the button options in your
+ * event handler, and the column header will be automatically updated to
+ * reflect them. This is useful if you want to implement something like a
+ * toggle button.
+ *
+ *
+ * @param options {Object} Options:
+ * buttonCssClass: an extra CSS class to add to the menu button
+ * buttonImage: a url to the menu button image (default '../images/down.gif')
+ * @class Slick.Plugins.HeaderButtons
+ * @constructor
+ */
+ function HeaderMenu(options) {
+ var _grid;
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _defaults = {
+ buttonCssClass: null,
+ buttonImage: "../images/down.gif"
+ };
+ var $menu;
+ var $activeHeaderColumn;
+
+
+ function init(grid) {
+ options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ _handler
+ .subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered)
+ .subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy);
+
+ // Force the grid to re-render the header now that the events are hooked up.
+ _grid.setColumns(_grid.getColumns());
+
+ // Hide the menu on outside click.
+ $(document.body).bind("mousedown", handleBodyMouseDown);
+ }
+
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ $(document.body).unbind("mousedown", handleBodyMouseDown);
+ }
+
+
+ function handleBodyMouseDown(e) {
+ if ($menu && $menu[0] != e.target && !$.contains($menu[0], e.target)) {
+ hideMenu();
+ }
+ }
+
+
+ function hideMenu() {
+ if ($menu) {
+ $menu.remove();
+ $menu = null;
+ $activeHeaderColumn
+ .removeClass("slick-header-column-active");
+ }
+ }
+
+ function handleHeaderCellRendered(e, args) {
+ var column = args.column;
+ var menu = column.header && column.header.menu;
+
+ if (menu) {
+ var $el = $("<div></div>")
+ .addClass("slick-header-menubutton")
+ .data("column", column)
+ .data("menu", menu);
+
+ if (options.buttonCssClass) {
+ $el.addClass(options.buttonCssClass);
+ }
+
+ if (options.buttonImage) {
+ $el.css("background-image", "url(" + options.buttonImage + ")");
+ }
+
+ if (menu.tooltip) {
+ $el.attr("title", menu.tooltip);
+ }
+
+ $el
+ .bind("click", showMenu)
+ .appendTo(args.node);
+ }
+ }
+
+
+ function handleBeforeHeaderCellDestroy(e, args) {
+ var column = args.column;
+
+ if (column.header && column.header.menu) {
+ $(args.node).find(".slick-header-menubutton").remove();
+ }
+ }
+
+
+ function showMenu(e) {
+ var $menuButton = $(this);
+ var menu = $menuButton.data("menu");
+ var columnDef = $menuButton.data("column");
+
+ // Let the user modify the menu or cancel altogether,
+ // or provide alternative menu implementation.
+ if (_self.onBeforeMenuShow.notify({
+ "grid": _grid,
+ "column": columnDef,
+ "menu": menu
+ }, e, _self) == false) {
+ return;
+ }
+
+
+ if (!$menu) {
+ $menu = $("<div class='slick-header-menu'></div>")
+ .appendTo(document.body);
+ }
+ $menu.empty();
+
+
+ // Construct the menu items.
+ for (var i = 0; i < menu.items.length; i++) {
+ var item = menu.items[i];
+
+ var $li = $("<div class='slick-header-menuitem'></div>")
+ .data("command", item.command || '')
+ .data("column", columnDef)
+ .data("item", item)
+ .bind("click", handleMenuItemClick)
+ .appendTo($menu);
+
+ if (item.disabled) {
+ $li.addClass("slick-header-menuitem-disabled");
+ }
+
+ if (item.tooltip) {
+ $li.attr("title", item.tooltip);
+ }
+
+ var $icon = $("<div class='slick-header-menuicon'></div>")
+ .appendTo($li);
+
+ if (item.iconCssClass) {
+ $icon.addClass(item.iconCssClass);
+ }
+
+ if (item.iconImage) {
+ $icon.css("background-image", "url(" + item.iconImage + ")");
+ }
+
+ $("<span class='slick-header-menucontent'></span>")
+ .text(item.title)
+ .appendTo($li);
+ }
+
+
+ // Position the menu.
+ $menu
+ .css("top", $(this).offset().top + $(this).height())
+ .css("left", $(this).offset().left);
+
+
+ // Mark the header as active to keep the highlighting.
+ $activeHeaderColumn = $menuButton.closest(".slick-header-column");
+ $activeHeaderColumn
+ .addClass("slick-header-column-active");
+ }
+
+
+ function handleMenuItemClick(e) {
+ var command = $(this).data("command");
+ var columnDef = $(this).data("column");
+ var item = $(this).data("item");
+
+ if (item.disabled) {
+ return;
+ }
+
+ hideMenu();
+
+ if (command != null && command != '') {
+ _self.onCommand.notify({
+ "grid": _grid,
+ "column": columnDef,
+ "command": command,
+ "item": item
+ }, e, _self);
+ }
+
+ // Stop propagation so that it doesn't register as a header click event.
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy,
+
+ "onBeforeMenuShow": new Slick.Event(),
+ "onCommand": new Slick.Event()
+ });
+ }
+})(jQuery);
View
138 scrapedump/static/js/plugins/slick.rowmovemanager.js
@@ -0,0 +1,138 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "RowMoveManager": RowMoveManager
+ }
+ });
+
+ function RowMoveManager(options) {
+ var _grid;
+ var _canvas;
+ var _dragging;
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _defaults = {
+ cancelEditOnDrag: false
+ };
+
+ function init(grid) {
+ options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ _canvas = _grid.getCanvasNode();
+ _handler
+ .subscribe(_grid.onDragInit, handleDragInit)
+ .subscribe(_grid.onDragStart, handleDragStart)
+ .subscribe(_grid.onDrag, handleDrag)
+ .subscribe(_grid.onDragEnd, handleDragEnd);
+ }
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ }
+
+ function handleDragInit(e, dd) {
+ // prevent the grid from cancelling drag'n'drop by default
+ e.stopImmediatePropagation();
+ }
+
+ function handleDragStart(e, dd) {
+ var cell = _grid.getCellFromEvent(e);
+
+ if (options.cancelEditOnDrag && _grid.getEditorLock().isActive()) {
+ _grid.getEditorLock().cancelCurrentEdit();
+ }
+
+ if (_grid.getEditorLock().isActive() || !/move|selectAndMove/.test(_grid.getColumns()[cell.cell].behavior)) {
+ return false;
+ }
+
+ _dragging = true;
+ e.stopImmediatePropagation();
+
+ var selectedRows = _grid.getSelectedRows();
+
+ if (selectedRows.length == 0 || $.inArray(cell.row, selectedRows) == -1) {
+ selectedRows = [cell.row];
+ _grid.setSelectedRows(selectedRows);
+ }
+
+ var rowHeight = _grid.getOptions().rowHeight;
+
+ dd.selectedRows = selectedRows;
+
+ dd.selectionProxy = $("<div class='slick-reorder-proxy'/>")
+ .css("position", "absolute")
+ .css("zIndex", "99999")
+ .css("width", $(_canvas).innerWidth())
+ .css("height", rowHeight * selectedRows.length)
+ .appendTo(_canvas);
+
+ dd.guide = $("<div class='slick-reorder-guide'/>")
+ .css("position", "absolute")
+ .css("zIndex", "99998")
+ .css("width", $(_canvas).innerWidth())
+ .css("top", -1000)
+ .appendTo(_canvas);
+
+ dd.insertBefore = -1;
+ }
+
+ function handleDrag(e, dd) {
+ if (!_dragging) {
+ return;
+ }
+
+ e.stopImmediatePropagation();
+
+ var top = e.pageY - $(_canvas).offset().top;
+ dd.selectionProxy.css("top", top - 5);
+
+ var insertBefore = Math.max(0, Math.min(Math.round(top / _grid.getOptions().rowHeight), _grid.getDataLength()));
+ if (insertBefore !== dd.insertBefore) {
+ var eventData = {
+ "rows": dd.selectedRows,
+ "insertBefore": insertBefore
+ };
+
+ if (_self.onBeforeMoveRows.notify(eventData) === false) {
+ dd.guide.css("top", -1000);
+ dd.canMove = false;
+ } else {
+ dd.guide.css("top", insertBefore * _grid.getOptions().rowHeight);
+ dd.canMove = true;
+ }
+
+ dd.insertBefore = insertBefore;
+ }
+ }
+
+ function handleDragEnd(e, dd) {
+ if (!_dragging) {
+ return;
+ }
+ _dragging = false;
+ e.stopImmediatePropagation();
+
+ dd.guide.remove();
+ dd.selectionProxy.remove();
+
+ if (dd.canMove) {
+ var eventData = {
+ "rows": dd.selectedRows,
+ "insertBefore": dd.insertBefore
+ };
+ // TODO: _grid.remapCellCssClasses ?
+ _self.onMoveRows.notify(eventData);
+ }
+ }
+
+ $.extend(this, {
+ "onBeforeMoveRows": new Slick.Event(),
+ "onMoveRows": new Slick.Event(),
+
+ "init": init,
+ "destroy": destroy
+ });
+ }
+})(jQuery);
View
187 scrapedump/static/js/plugins/slick.rowselectionmodel.js
@@ -0,0 +1,187 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "RowSelectionModel": RowSelectionModel
+ }
+ });
+
+ function RowSelectionModel(options) {
+ var _grid;
+ var _ranges = [];
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _inHandler;
+ var _options;
+ var _defaults = {
+ selectActiveRow: true
+ };
+
+ function init(grid) {
+ _options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ _handler.subscribe(_grid.onActiveCellChanged,
+ wrapHandler(handleActiveCellChange));
+ _handler.subscribe(_grid.onKeyDown,
+ wrapHandler(handleKeyDown));
+ _handler.subscribe(_grid.onClick,
+ wrapHandler(handleClick));
+ }
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ }
+
+ function wrapHandler(handler) {
+ return function () {
+ if (!_inHandler) {
+ _inHandler = true;
+ handler.apply(this, arguments);
+ _inHandler = false;
+ }
+ };
+ }
+
+ function rangesToRows(ranges) {
+ var rows = [];
+ for (var i = 0; i < ranges.length; i++) {
+ for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
+ rows.push(j);
+ }
+ }
+ return rows;
+ }
+
+ function rowsToRanges(rows) {
+ var ranges = [];
+ var lastCell = _grid.getColumns().length - 1;
+ for (var i = 0; i < rows.length; i++) {
+ ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell));
+ }
+ return ranges;
+ }
+
+ function getRowsRange(from, to) {
+ var i, rows = [];
+ for (i = from; i <= to; i++) {
+ rows.push(i);
+ }
+ for (i = to; i < from; i++) {
+ rows.push(i);
+ }
+ return rows;
+ }
+
+ function getSelectedRows() {
+ return rangesToRows(_ranges);
+ }
+
+ function setSelectedRows(rows) {
+ setSelectedRanges(rowsToRanges(rows));
+ }
+
+ function setSelectedRanges(ranges) {
+ _ranges = ranges;
+ _self.onSelectedRangesChanged.notify(_ranges);
+ }
+
+ function getSelectedRanges() {
+ return _ranges;
+ }
+
+ function handleActiveCellChange(e, data) {
+ if (_options.selectActiveRow) {
+ setSelectedRanges([new Slick.Range(data.row, 0, data.row, _grid.getColumns().length - 1)]);
+ }
+ }
+
+ function handleKeyDown(e) {
+ var activeRow = _grid.getActiveCell();
+ if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which == 38 || e.which == 40)) {
+ var selectedRows = getSelectedRows();
+ selectedRows.sort(function (x, y) {
+ return x - y
+ });
+
+ if (!selectedRows.length) {
+ selectedRows = [activeRow.row];
+ }
+
+ var top = selectedRows[0];
+ var bottom = selectedRows[selectedRows.length - 1];
+ var active;
+
+ if (e.which == 40) {
+ active = activeRow.row < bottom || top == bottom ? ++bottom : ++top;
+ } else {
+ active = activeRow.row < bottom ? --bottom : --top;
+ }
+
+ if (active >= 0 && active < _grid.getDataLength()) {
+ _grid.scrollRowIntoView(active);
+ _ranges = rowsToRanges(getRowsRange(top, bottom));
+ setSelectedRanges(_ranges);
+ }
+
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ }
+
+ function handleClick(e) {
+ var cell = _grid.getCellFromEvent(e);
+ if (!cell || !_grid.canCellBeActive(cell.row, cell.cell)) {
+ return false;
+ }
+
+ var selection = rangesToRows(_ranges);
+ var idx = $.inArray(cell.row, selection);
+
+ if (!e.ctrlKey && !e.shiftKey && !e.metaKey) {
+ return false;
+ }
+ else if (_grid.getOptions().multiSelect) {
+ if (idx === -1 && (e.ctrlKey || e.metaKey)) {
+ selection.push(cell.row);
+ _grid.setActiveCell(cell.row, cell.cell);
+ } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) {
+ selection = $.grep(selection, function (o, i) {
+ return (o !== cell.row);
+ });
+ _grid.setActiveCell(cell.row, cell.cell);
+ } else if (selection.length && e.shiftKey) {
+ var last = selection.pop();
+ var from = Math.min(cell.row, last);
+ var to = Math.max(cell.row, last);
+ selection = [];
+ for (var i = from; i <= to; i++) {
+ if (i !== last) {
+ selection.push(i);
+ }
+ }
+ selection.push(last);
+ _grid.setActiveCell(cell.row, cell.cell);
+ }
+ }
+
+ _ranges = rowsToRanges(selection);
+ setSelectedRanges(_ranges);
+ e.stopImmediatePropagation();
+
+ return true;
+ }
+
+ $.extend(this, {
+ "getSelectedRows": getSelectedRows,
+ "setSelectedRows": setSelectedRows,
+
+ "getSelectedRanges": getSelectedRanges,
+ "setSelectedRanges": setSelectedRanges,
+
+ "init": init,
+ "destroy": destroy,
+
+ "onSelectedRangesChanged": new Slick.Event()
+ });
+ }
+})(jQuery);
View
430 scrapedump/static/js/slick.core.js
@@ -0,0 +1,430 @@
+/***
+ * Contains core SlickGrid classes.
+ * @module Core
+ * @namespace Slick
+ */
+
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "Event": Event,
+ "EventData": EventData,
+ "EventHandler": EventHandler,
+ "Range": Range,
+ "NonDataRow": NonDataItem,
+ "Group": Group,
+ "GroupTotals": GroupTotals,
+ "EditorLock": EditorLock,
+
+ /***
+ * A global singleton editor lock.
+ * @class GlobalEditorLock
+ * @static
+ * @constructor
+ */
+ "GlobalEditorLock": new EditorLock()
+ }
+ });
+
+ /***
+ * An event object for passing data to event handlers and letting them control propagation.
+ * <p>This is pretty much identical to how W3C and jQuery implement events.</p>
+ * @class EventData
+ * @constructor
+ */
+ function EventData() {
+ var isPropagationStopped = false;
+ var isImmediatePropagationStopped = false;
+
+ /***
+ * Stops event from propagating up the DOM tree.
+ * @method stopPropagation
+ */
+ this.stopPropagation = function () {
+ isPropagationStopped = true;
+ };
+
+ /***
+ * Returns whether stopPropagation was called on this event object.
+ * @method isPropagationStopped
+ * @return {Boolean}
+ */
+ this.isPropagationStopped = function () {
+ return isPropagationStopped;
+ };
+
+ /***
+ * Prevents the rest of the handlers from being executed.
+ * @method stopImmediatePropagation
+ */
+ this.stopImmediatePropagation = function () {
+ isImmediatePropagationStopped = true;
+ };
+
+ /***
+ * Returns whether stopImmediatePropagation was called on this event object.\
+ * @method isImmediatePropagationStopped
+ * @return {Boolean}
+ */
+ this.isImmediatePropagationStopped = function () {
+ return isImmediatePropagationStopped;
+ }
+ }
+
+ /***
+ * A simple publisher-subscriber implementation.
+ * @class Event
+ * @constructor
+ */
+ function Event() {
+ var handlers = [];
+
+ /***
+ * Adds an event handler to be called when the event is fired.
+ * <p>Event handler will receive two arguments - an <code>EventData</code> and the <code>data</code>
+ * object the event was fired with.<p>
+ * @method subscribe
+ * @param fn {Function} Event handler.
+ */
+ this.subscribe = function (fn) {
+ handlers.push(fn);
+ };
+
+ /***
+ * Removes an event handler added with <code>subscribe(fn)</code>.
+ * @method unsubscribe
+ * @param fn {Function} Event handler to be removed.
+ */
+ this.unsubscribe = function (fn) {
+ for (var i = handlers.length - 1; i >= 0; i--) {
+ if (handlers[i] === fn) {
+ handlers.splice(i, 1);
+ }
+ }
+ };
+
+ /***
+ * Fires an event notifying all subscribers.
+ * @method notify
+ * @param args {Object} Additional data object to be passed to all handlers.
+ * @param e {EventData}
+ * Optional.
+ * An <code>EventData</code> object to be passed to all handlers.
+ * For DOM events, an existing W3C/jQuery event object can be passed in.
+ * @param scope {Object}
+ * Optional.
+ * The scope ("this") within which the handler will be executed.
+ * If not specified, the scope will be set to the <code>Event</code> instance.
+ */
+ this.notify = function (args, e, scope) {
+ e = e || new EventData();
+ scope = scope || this;
+
+ var returnValue;
+ for (var i = 0; i < handlers.length && !(e.isPropagationStopped() || e.isImmediatePropagationStopped()); i++) {
+ returnValue = handlers[i].call(scope, e, args);
+ }
+
+ return returnValue;
+ };
+ }
+
+ function EventHandler() {
+ var handlers = [];
+
+ this.subscribe = function (event, handler) {
+ handlers.push({
+ event: event,
+ handler: handler
+ });
+ event.subscribe(handler);
+
+ return this; // allow chaining
+ };
+
+ this.unsubscribe = function (event, handler) {
+ var i = handlers.length;
+ while (i--) {
+ if (handlers[i].event === event &&
+ handlers[i].handler === handler) {
+ handlers.splice(i, 1);
+ event.unsubscribe(handler);
+ return;
+ }
+ }
+
+ return this; // allow chaining
+ };
+
+ this.unsubscribeAll = function () {
+ var i = handlers.length;
+ while (i--) {
+ handlers[i].event.unsubscribe(handlers[i].handler);
+ }
+ handlers = [];
+
+ return this; // allow chaining
+ }
+ }
+
+ /***
+ * A structure containing a range of cells.
+ * @class Range
+ * @constructor
+ * @param fromRow {Integer} Starting row.
+ * @param fromCell {Integer} Starting cell.
+ * @param toRow {Integer} Optional. Ending row. Defaults to <code>fromRow</code>.
+ * @param toCell {Integer} Optional. Ending cell. Defaults to <code>fromCell</code>.
+ */
+ function Range(fromRow, fromCell, toRow, toCell) {
+ if (toRow === undefined && toCell === undefined) {
+ toRow = fromRow;
+ toCell = fromCell;
+ }
+
+ /***
+ * @property fromRow
+ * @type {Integer}
+ */
+ this.fromRow = Math.min(fromRow, toRow);
+
+ /***
+ * @property fromCell
+ * @type {Integer}
+ */
+ this.fromCell = Math.min(fromCell, toCell);
+
+ /***
+ * @property toRow
+ * @type {Integer}
+ */
+ this.toRow = Math.max(fromRow, toRow);
+
+ /***
+ * @property toCell
+ * @type {Integer}
+ */
+ this.toCell = Math.max(fromCell, toCell);
+
+ /***
+ * Returns whether a range represents a single row.
+ * @method isSingleRow
+ * @return {Boolean}
+ */
+ this.isSingleRow = function () {
+ return this.fromRow == this.toRow;