+
+
diff --git a/webapp/static/css/imgareaselect-default.css b/webapp/static/css/imgareaselect-default.css
index bd601ea76..f17d32789 100644
--- a/webapp/static/css/imgareaselect-default.css
+++ b/webapp/static/css/imgareaselect-default.css
@@ -25,17 +25,24 @@
}
.imgareaselect-box{
- /* placeholder */
+/* these properties moved to imgareaselect-area -JBM 8/16/14
+ background-color: #000;
+ filter: alpha(opacity=50);
+ opacity: 0.3;
+*/
+}
+.imgareaselect-area{
background-color: #000;
filter: alpha(opacity=50);
opacity: 0.3;
+ overflow: hidden;
}
.imgareaselect-handle {
background-color: #fff;
border: solid 1px #000;
filter: alpha(opacity=50);
- opacity: 0.5;
+ opacity: 0.3;
}
.imgareaselect-outer {
diff --git a/webapp/static/css/tabula_web.css b/webapp/static/css/tabula_web.css
index f1f024ffa..d22c18c1e 100644
--- a/webapp/static/css/tabula_web.css
+++ b/webapp/static/css/tabula_web.css
@@ -99,8 +99,17 @@
display: none;
}
+ .advanced-options-shown #advanced-options{
+ display: block;
+ }
+
+ .advanced-options-shown #basic-options .show-advanced-options{
+ display: none;
+ }
+
.toggle-advanced-options{
cursor: select;
+ color: #666;
}
#data-modal .modal-body {
@@ -171,6 +180,13 @@
top: 10px !important;
}
+/* selections */
+.repeat-lassos.btn {
+ position: absolute;
+ right: -165px;
+ bottom: -35px;
+}
+
/* front page */
ul#uploadedfiles {
diff --git a/webapp/static/js/ZeroClipboard.js b/webapp/static/js/ZeroClipboard.js
deleted file mode 100644
index 4968be9d2..000000000
--- a/webapp/static/js/ZeroClipboard.js
+++ /dev/null
@@ -1,303 +0,0 @@
-/*!
- * zeroclipboard
- * The Zero Clipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie, and a JavaScript interface.
- * Copyright 2012 Jon Rohan, James M. Greene, .
- * Released under the MIT license
- * http://jonrohan.github.com/ZeroClipboard/
- * v1.1.0
- */(function() {
- "use strict";
- var ZeroClipboard = {};
- ZeroClipboard.Client = function(query) {
- if (ZeroClipboard._client) return ZeroClipboard._client;
- this.handlers = {};
- if (ZeroClipboard.detectFlashSupport()) this.bridge();
- if (query) this.glue(query);
- ZeroClipboard._client = this;
- };
- function _elementMouseOver() {
- ZeroClipboard._client.setCurrent(this);
- }
- ZeroClipboard.Client.prototype.glue = function(query) {
- function _addEventHandler(element, method, func) {
- if (element.addEventListener) {
- element.addEventListener(method, func, false);
- } else if (element.attachEvent) {
- element.attachEvent(method, func);
- }
- }
- var elements = ZeroClipboard.$(query);
- for (var i = 0; i < elements.length; i++) {
- _addEventHandler(elements[i], "mouseover", _elementMouseOver);
- }
- };
- ZeroClipboard.Client.prototype.unglue = function(query) {
- function _removeEventHandler(element, method, func) {
- if (element.removeEventListener) {
- element.removeEventListener(method, func, false);
- } else if (element.detachEvent) {
- element.detachEvent(method, func);
- }
- }
- var elements = ZeroClipboard.$(query);
- for (var i = 0; i < elements.length; i++) {
- _removeEventHandler(elements[i], "mouseover", _elementMouseOver);
- }
- };
- ZeroClipboard.Client.prototype.bridge = function() {
- this.htmlBridge = ZeroClipboard.$("#global-zeroclipboard-html-bridge");
- if (this.htmlBridge.length) {
- this.htmlBridge = this.htmlBridge[0];
- this.flashBridge = document["global-zeroclipboard-flash-bridge"];
- return;
- }
- function noCache(path) {
- return (path.indexOf("?") >= 0 ? "&" : "?") + "nocache=" + (new Date).getTime();
- }
- var html = ' ';
- this.htmlBridge = document.createElement("div");
- this.htmlBridge.id = "global-zeroclipboard-html-bridge";
- this.htmlBridge.setAttribute("class", "global-zeroclipboard-container");
- this.htmlBridge.setAttribute("data-clipboard-ready", false);
- this.htmlBridge.style.position = "absolute";
- this.htmlBridge.style.left = "-9999px";
- this.htmlBridge.style.top = "-9999px";
- this.htmlBridge.style.width = "15px";
- this.htmlBridge.style.height = "15px";
- this.htmlBridge.style.zIndex = "9999";
- this.htmlBridge.innerHTML = html;
- document.body.appendChild(this.htmlBridge);
- this.flashBridge = document["global-zeroclipboard-flash-bridge"];
- };
- ZeroClipboard.Client.prototype.resetBridge = function() {
- this.htmlBridge.style.left = "-9999px";
- this.htmlBridge.style.top = "-9999px";
- this.htmlBridge.removeAttribute("title");
- this.htmlBridge.removeAttribute("data-clipboard-text");
- ZeroClipboard.currentElement.removeClass("zeroclipboard-is-active");
- delete ZeroClipboard.currentElement;
- };
- ZeroClipboard.Client.prototype.ready = function() {
- return !!this.htmlBridge.getAttribute("data-clipboard-ready");
- };
- function _getCursor(el) {
- var y = el.style.cursor;
- if (el.currentStyle) y = el.currentStyle.cursor; else if (window.getComputedStyle) y = document.defaultView.getComputedStyle(el, null).getPropertyValue("cursor");
- if (y == "auto") {
- var possiblePointers = [ "a" ];
- for (var i = 0; i < possiblePointers.length; i++) {
- if (el.tagName.toLowerCase() == possiblePointers[i]) {
- return "pointer";
- }
- }
- }
- return y;
- }
- ZeroClipboard.Client.prototype.setCurrent = function(element) {
- ZeroClipboard.currentElement = element;
- this.reposition();
- if (element.getAttribute("data-clipboard-text")) {
- this.setText(element.getAttribute("data-clipboard-text"));
- }
- if (element.getAttribute("title")) {
- this.setTitle(element.getAttribute("title"));
- }
- if (_getCursor(element) == "pointer") {
- this.setHandCursor(true);
- } else {
- this.setHandCursor(false);
- }
- };
- ZeroClipboard.Client.prototype.reposition = function() {
- var pos = $(ZeroClipboard.currentElement).offset();
- pos.height = $(ZeroClipboard.currentElement).outerHeight();
- pos.width = $(ZeroClipboard.currentElement).outerWidth();
- this.htmlBridge.style.top = pos.top + "px";
- this.htmlBridge.style.left = pos.left + "px";
- this.htmlBridge.style.width = pos.width + "px";
- this.htmlBridge.style.height = pos.height + "px";
- this.htmlBridge.style.zIndex = 9999;
- this.setSize(pos.width, pos.height);
- };
- ZeroClipboard.Client.prototype.setText = function(newText) {
- if (newText && newText !== "") {
- this.htmlBridge.setAttribute("data-clipboard-text", newText);
- if (this.ready()) this.flashBridge.setText(newText);
- }
- };
- ZeroClipboard.Client.prototype.setTitle = function(newTitle) {
- if (newTitle && newTitle !== "") this.htmlBridge.setAttribute("title", newTitle);
- };
- ZeroClipboard.Client.prototype.setSize = function(width, height) {
- if (this.ready()) this.flashBridge.setSize(width, height);
- };
- ZeroClipboard.Client.prototype.setHandCursor = function(enabled) {
- if (this.ready()) this.flashBridge.setHandCursor(enabled);
- };
- ZeroClipboard.version = "1.1.0";
- ZeroClipboard.moviePath = "ZeroClipboard.swf";
- ZeroClipboard._client = null;
- ZeroClipboard.setMoviePath = function(path) {
- this.moviePath = path;
- };
- ZeroClipboard.destroy = function() {
- var query = ZeroClipboard.$("#global-zeroclipboard-html-bridge");
- if (!query.length) return;
- delete ZeroClipboard._client;
- var bridge = query[0];
- bridge.parentNode.removeChild(bridge);
- };
- ZeroClipboard.detectFlashSupport = function() {
- var hasFlash = false;
- try {
- if (new ActiveXObject("ShockwaveFlash.ShockwaveFlash")) {
- hasFlash = true;
- }
- } catch (error) {
- if (navigator.mimeTypes["application/x-shockwave-flash"]) {
- hasFlash = true;
- }
- }
- return hasFlash;
- };
- ZeroClipboard.dispatch = function(eventName, args) {
- ZeroClipboard._client.receiveEvent(eventName, args);
- };
- ZeroClipboard.Client.prototype.on = function(eventName, func) {
- var events = eventName.toString().split(/\s/g);
- for (var i = 0; i < events.length; i++) {
- eventName = events[i].toLowerCase().replace(/^on/, "");
- if (!this.handlers[eventName]) this.handlers[eventName] = [];
- this.handlers[eventName].push(func);
- }
- if (this.handlers.noflash && !ZeroClipboard.detectFlashSupport()) {
- this.receiveEvent("onNoFlash", null);
- }
- };
- ZeroClipboard.Client.prototype.addEventListener = function(eventName, func) {
- this.on(eventName, func);
- };
- ZeroClipboard.Client.prototype.receiveEvent = function(eventName, args) {
- eventName = eventName.toString().toLowerCase().replace(/^on/, "");
- switch (eventName) {
- case "load":
- if (args && parseFloat(args.flashVersion.replace(",", ".").replace(/[^0-9\.]/gi, "")) < 10) {
- this.receiveEvent("onWrongFlash", {
- flashVersion: args.flashVersion
- });
- return;
- }
- this.htmlBridge.setAttribute("data-clipboard-ready", true);
- break;
- case "mouseover":
- ZeroClipboard.currentElement.addClass("hover");
- break;
- case "mouseout":
- ZeroClipboard.currentElement.removeClass("hover");
- this.resetBridge();
- break;
- case "mousedown":
- ZeroClipboard.currentElement.addClass("active");
- break;
- case "mouseup":
- ZeroClipboard.currentElement.removeClass("active");
- break;
- }
- if (this.handlers[eventName]) {
- for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) {
- var func = this.handlers[eventName][idx];
- if (typeof func == "function") {
- func(this, args);
- } else if (typeof func == "string") {
- window[func](this, args);
- }
- }
- }
- };
- ZeroClipboard.getDOMObjectPosition = function(obj) {
- var info = {
- left: 0,
- top: 0,
- width: obj.width ? obj.width : obj.offsetWidth,
- height: obj.height ? obj.height : obj.offsetHeight,
- zIndex: 9999
- };
- if (obj.style.zIndex) {
- info.zIndex = parseInt(element.style.zIndex, 10);
- }
- while (obj) {
- info.left += obj.offsetLeft;
- info.left += obj.style.borderLeftWidth ? parseInt(obj.style.borderLeftWidth, 10) : 0;
- info.top += obj.offsetTop;
- info.top += obj.style.borderTopWidth ? parseInt(obj.style.borderTopWidth, 10) : 0;
- obj = obj.offsetParent;
- }
- return info;
- };
- function elementWrapper(element) {
- if (!element || element.addClass) return element;
- element.addClass = function(value) {
- if (value && typeof value === "string") {
- var classNames = (value || "").split(/\s+/);
- var elem = this;
- if (elem.nodeType === 1) {
- if (!elem.className) {
- elem.className = value;
- } else {
- var className = " " + elem.className + " ", setClass = elem.className;
- for (var c = 0, cl = classNames.length; c < cl; c++) {
- if (className.indexOf(" " + classNames[c] + " ") < 0) {
- setClass += " " + classNames[c];
- }
- }
- elem.className = setClass.replace(/^\s+|\s+$/g, "");
- }
- }
- }
- return this;
- };
- element.removeClass = function(value) {
- if (value && typeof value === "string" || value === undefined) {
- var classNames = (value || "").split(/\s+/);
- var elem = this;
- if (elem.nodeType === 1 && elem.className) {
- if (value) {
- var className = (" " + elem.className + " ").replace(/[\n\t]/g, " ");
- for (var c = 0, cl = classNames.length; c < cl; c++) {
- className = className.replace(" " + classNames[c] + " ", " ");
- }
- elem.className = className.replace(/^\s+|\s+$/g, "");
- } else {
- elem.className = "";
- }
- }
- }
- return this;
- };
- return element;
- }
- ZeroClipboard.$ = function(query) {
- var ZeroClipboardSelect = function(s, n) {
- return n.querySelectorAll(s);
- }, result;
- if (typeof Sizzle === "function") {
- ZeroClipboardSelect = function(s, n) {
- return Sizzle.uniqueSort(Sizzle(s, n));
- };
- }
- if (typeof query === "string") {
- result = ZeroClipboardSelect(query, document);
- if (result.length === 0) result = [ document.getElementById(query) ];
- }
- var newresult = [];
- for (var i = 0; i < result.length; i++) {
- if (result[i] !== null) newresult.push(elementWrapper(result[i]));
- }
- return newresult;
- };
- if (typeof module !== "undefined") {
- module.exports = ZeroClipboard;
- } else {
- window.ZeroClipboard = ZeroClipboard;
- }
-})();
diff --git a/webapp/static/js/backbone-min.js b/webapp/static/js/backbone-min.js
deleted file mode 100644
index 3541019c5..000000000
--- a/webapp/static/js/backbone-min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-(function(){var t=this;var e=t.Backbone;var i=[];var r=i.push;var s=i.slice;var n=i.splice;var a;if(typeof exports!=="undefined"){a=exports}else{a=t.Backbone={}}a.VERSION="1.0.0";var h=t._;if(!h&&typeof require!=="undefined")h=require("underscore");a.$=t.jQuery||t.Zepto||t.ender||t.$;a.noConflict=function(){t.Backbone=e;return this};a.emulateHTTP=false;a.emulateJSON=false;var o=a.Events={on:function(t,e,i){if(!l(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,i){if(!l(this,"once",t,[e,i])||!e)return this;var r=this;var s=h.once(function(){r.off(t,s);e.apply(this,arguments)});s._callback=e;return this.on(t,s,i)},off:function(t,e,i){var r,s,n,a,o,u,c,f;if(!this._events||!l(this,"off",t,[e,i]))return this;if(!t&&!e&&!i){this._events={};return this}a=t?[t]:h.keys(this._events);for(o=0,u=a.length;o").attr(t);this.setElement(e,false)}else{this.setElement(h.result(this,"el"),false)}}});a.sync=function(t,e,i){var r=k[t];h.defaults(i||(i={}),{emulateHTTP:a.emulateHTTP,emulateJSON:a.emulateJSON});var s={type:r,dataType:"json"};if(!i.url){s.url=h.result(e,"url")||U()}if(i.data==null&&e&&(t==="create"||t==="update"||t==="patch")){s.contentType="application/json";s.data=JSON.stringify(i.attrs||e.toJSON(i))}if(i.emulateJSON){s.contentType="application/x-www-form-urlencoded";s.data=s.data?{model:s.data}:{}}if(i.emulateHTTP&&(r==="PUT"||r==="DELETE"||r==="PATCH")){s.type="POST";if(i.emulateJSON)s.data._method=r;var n=i.beforeSend;i.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",r);if(n)return n.apply(this,arguments)}}if(s.type!=="GET"&&!i.emulateJSON){s.processData=false}if(s.type==="PATCH"&&window.ActiveXObject&&!(window.external&&window.external.msActiveXFilteringEnabled)){s.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var o=i.xhr=a.ajax(h.extend(s,i));e.trigger("request",e,o,i);return o};var k={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};a.ajax=function(){return a.$.ajax.apply(a.$,arguments)};var S=a.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var $=/\((.*?)\)/g;var T=/(\(\?)?:\w+/g;var H=/\*\w+/g;var A=/[\-{}\[\]+?.,\\\^$|#\s]/g;h.extend(S.prototype,o,{initialize:function(){},route:function(t,e,i){if(!h.isRegExp(t))t=this._routeToRegExp(t);if(h.isFunction(e)){i=e;e=""}if(!i)i=this[e];var r=this;a.history.route(t,function(s){var n=r._extractParameters(t,s);i&&i.apply(r,n);r.trigger.apply(r,["route:"+e].concat(n));r.trigger("route",e,n);a.history.trigger("route",r,e,n)});return this},navigate:function(t,e){a.history.navigate(t,e);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=h.result(this,"routes");var t,e=h.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(A,"\\$&").replace($,"(?:$1)?").replace(T,function(t,e){return e?t:"([^/]+)"}).replace(H,"(.*?)");return new RegExp("^"+t+"$")},_extractParameters:function(t,e){var i=t.exec(e).slice(1);return h.map(i,function(t){return t?decodeURIComponent(t):null})}});var I=a.History=function(){this.handlers=[];h.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var N=/^[#\/]|\s+$/g;var P=/^\/+|\/+$/g;var O=/msie [\w.]+/;var C=/\/$/;I.started=false;h.extend(I.prototype,o,{interval:50,getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=this.location.pathname;var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.substr(i.length)}else{t=this.getHash()}}return t.replace(N,"")},start:function(t){if(I.started)throw new Error("Backbone.history has already been started");I.started=true;this.options=h.extend({},{root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var e=this.getFragment();var i=document.documentMode;var r=O.exec(navigator.userAgent.toLowerCase())&&(!i||i<=7);this.root=("/"+this.root+"/").replace(P,"/");if(r&&this._wantsHashChange){this.iframe=a.$('').hide().appendTo("body")[0].contentWindow;this.navigate(e)}if(this._hasPushState){a.$(window).on("popstate",this.checkUrl)}else if(this._wantsHashChange&&"onhashchange"in window&&!r){a.$(window).on("hashchange",this.checkUrl)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}this.fragment=e;var s=this.location;var n=s.pathname.replace(/[^\/]$/,"$&/")===this.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!n){this.fragment=this.getFragment(null,true);this.location.replace(this.root+this.location.search+"#"+this.fragment);return true}else if(this._wantsPushState&&this._hasPushState&&n&&s.hash){this.fragment=this.getHash().replace(N,"");this.history.replaceState({},document.title,this.root+this.fragment+s.search)}if(!this.options.silent)return this.loadUrl()},stop:function(){a.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);I.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getFragment(this.getHash(this.iframe))}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(t){var e=this.fragment=this.getFragment(t);var i=h.any(this.handlers,function(t){if(t.route.test(e)){t.callback(e);return true}});return i},navigate:function(t,e){if(!I.started)return false;if(!e||e===true)e={trigger:e};t=this.getFragment(t||"");if(this.fragment===t)return;this.fragment=t;var i=this.root+t;if(this._hasPushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,i)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getFragment(this.getHash(this.iframe))){if(!e.replace)this.iframe.document.open().close();this._updateHash(this.iframe.location,t,e.replace)}}else{return this.location.assign(i)}if(e.trigger)this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});a.history=new I;var j=function(t,e){var i=this;var r;if(t&&h.has(t,"constructor")){r=t.constructor}else{r=function(){return i.apply(this,arguments)}}h.extend(r,i,e);var s=function(){this.constructor=r};s.prototype=i.prototype;r.prototype=new s;if(t)h.extend(r.prototype,t);r.__super__=i.prototype;return r};d.extend=g.extend=S.extend=b.extend=I.extend=j;var U=function(){throw new Error('A "url" property or function must be specified')};var R=function(t,e){var i=e.error;e.error=function(r){if(i)i(t,r,e);t.trigger("error",t,r,e)}}}).call(this);
-/*
-//@ sourceMappingURL=backbone-min.map
-*/
\ No newline at end of file
diff --git a/webapp/static/js/jquery.imgareaselect.js b/webapp/static/js/jquery.imgareaselect.js
index c31f8e3bc..d57c55545 100644
--- a/webapp/static/js/jquery.imgareaselect.js
+++ b/webapp/static/js/jquery.imgareaselect.js
@@ -57,9 +57,6 @@ $.imgAreaSelect = function (img, options) {
/* Document element */
docElem = document.documentElement,
- /* User agent */
- ua = navigator.userAgent,
-
/* Image position (relative to viewport) */
left, top,
@@ -198,12 +195,13 @@ $.imgAreaSelect = function (img, options) {
$parent.append(this.$box);
this.$box/*.add($outer)*/.css({ position: this.position,
- overflow: 'hidden', zIndex: zIndex || '0' });
+ // overflow: 'hidden', -JBM 8/16/14
+ zIndex: zIndex || '0' });
this.$box.css({ zIndex: zIndex + 2 || 2 });
this.$closeBtn.css({ zIndex: zIndex + 3 || 3 });
this.$area.add(this.$border).css({ position: 'absolute', fontSize: 0 });
- $parent.append(this.$closeBtn);
+ this.$box.append(this.$closeBtn);
this.$box.append(this.$area.add(this.$border).add(this.$areaOpera)).append(this.$handles);
}; //ends the object
@@ -261,7 +259,11 @@ $.imgAreaSelect = function (img, options) {
*/
this.$area.add(this.$border).add(this.$handles).css({ left: 0, top: 0 });
- this.$closeBtn.css({left: left + this.selection.x2 + 4, top: top + this.selection.y1 - 20});
+ this.$closeBtn.css({ right: '-10px',
+ top: '-17px',
+ position: 'absolute',
+ 'text-align': 'right',
+ });
/* Set border dimensions */
this.$border
@@ -460,7 +462,9 @@ $.imgAreaSelect = function (img, options) {
y2: round(this.selection.y2 * sy),
width: round(this.selection.x2 * sx) - round(this.selection.x1 * sx),
height: round(this.selection.y2 * sy) - round(this.selection.y1 * sy) ,
- id: selections.indexOf(this)
+ id: selections.indexOf(this),
+ el: this.$box[0],
+ $el: this.$box
};
};
@@ -917,7 +921,9 @@ $.imgAreaSelect = function (img, options) {
options.onSelectChange(img, this.getSelection());
options.onSelectEnd(img, this.getSelection());
}
- options.onSelectCancel(img, this.getSelection(), index_of_this);
+ var selection = this.getSelection()
+ selection.id = index_of_this;
+ options.onSelectCancel(img, selection);
}
@@ -1197,7 +1203,7 @@ $.imgAreaSelect = function (img, options) {
if (options.show) {
shown = true;
- s.adjust();
+ adjust();
s.update();
s.$box/*.add(s.$outer)*/.hide().fadeIn(options.fadeSpeed||0);
}
@@ -1732,7 +1738,7 @@ $.fn.imgAreaSelect = function (options) {
$(this).data('imgAreaSelect').setOptions(options);
}
else if (!options.remove) {
- /* No exising instance -- create a new one */
+ /* No existing instance -- create a new one */
/*
* If neither the "enable" nor the "disable" option is present, add
diff --git a/webapp/static/js/pdf_view.js b/webapp/static/js/pdf_view.js
index b0ccabb71..84fdac622 100644
--- a/webapp/static/js/pdf_view.js
+++ b/webapp/static/js/pdf_view.js
@@ -1,165 +1,1008 @@
-Tabula = {};
+Tabula = Tabula || {};
var clip = null;
-$(document).ready(function() {
- ZeroClipboard.setMoviePath('/swf/ZeroClipboard.swf');
- clip = new ZeroClipboard.Client();
+PDF_ID = window.location.pathname.split('/')[2];
- clip.on('mousedown', function(client) {
- client.setText($('table').table2CSV({delivery: null}));
- $('#data-modal span').css('display', 'inline').delay(900).fadeOut('slow');
+// bootstrap 2 only, fix for multiple modal recursion error: http://stackoverflow.com/questions/13649459/twitter-bootstrap-multiple-modal-error
+$.fn.modal.Constructor.prototype.enforceFocus = function () {};
+
+ZeroClipboard.config( { swfPath: "/swf/ZeroClipboard.swf" } );
+
+Tabula.Page = Backbone.Model.extend({
+ // number: null, //set on initialize
+ // width: null, //set on initialize
+ // height: null, //set on initialize
+ // rotation: null, //set on initialize
+ pdf_document: null,
+ initialize: function(){
+ this.set('number_zero_indexed', this.get('number') - 1);
+ this.set('image_url', '/pdfs/' + PDF_ID + '/document_560_' + this.get('number') + '.png');
+ },
+});
+
+Tabula.Pages = Backbone.Collection.extend({
+ model: Tabula.Page,
+ url: null, //set on initialize
+ comparator: 'number',
+ initialize: function(){
+ this.url = '/pdfs/' + PDF_ID + '/pages.json?_=' + Math.round(+new Date()).toString();
+ },
+ hasText: function(){
+ has_text = false;
+ for (i=0;i < this.size();i++){
+ if(this.at(i).get('hasText')){
+ has_text = true;
+ break;
+ }
+ }
+ return has_text;
+ }
+
+
+});
+
+Tabula.Document = Backbone.Model.extend({
+ page_collection: null, //set on initialize
+ selections: null, //set on initialize
+ pdf_id: PDF_ID, //set on initialize
+ initialize: function(options){
+ this.page_collection = new Tabula.Pages([], {pdf_document: this})
+ this.selections = new Tabula.Selections([], {pdf_document: this})
+ }
+});
+
+Tabula.Selection = Backbone.Model.extend({
+ pdf_id: PDF_ID,
+
+ initialize: function(){
+ _.bindAll(this, 'queryForData', 'repeatLassos', 'toCoords')
+ },
+
+ queryForData: function(){
+ var selection_coords = this.toCoords();
+ Tabula.pdf_view.query = new Tabula.Query({list_of_coords: [selection_coords], extraction_method: this.get('extractionMethod')});
+ Tabula.pdf_view.createDataView();
+ Tabula.pdf_view.query.doQuery();
+ },
+
+ toCoords: function(){
+ var page = Tabula.pdf_view.pdf_document.page_collection.at(this.get('page_number') - 1);
+ var imageWidth = this.get('imageWidth');
+
+ var original_pdf_width = page.get('width');
+ var original_pdf_height = page.get('height');
+ var pdf_rotation = page.get('rotation');
+
+ var scale = (Math.abs(pdf_rotation) == 90 ? original_pdf_height : original_pdf_width) / imageWidth;
+
+ var selection_coords = {
+ x1: this.get('x1') * scale,
+ x2: this.get('x2') * scale,
+ y1: this.get('y1') * scale,
+ y2: this.get('y2') * scale,
+ page: this.get('page_number'),
+ extraction_method: this.get('extractionMethod') || 'guess',
+ selection_id: this.id
+ };
+ return selection_coords;
+ },
+
+ repeatLassos: function() {
+ Tabula.pdf_view.pdf_document.page_collection.each(_.bind(function(page){
+ if(this.get('page_number') < page.get('number')){ // for each page after this one,
+ imgAreaSelectAPIObj = Tabula.pdf_view.imgAreaSelects[page.get('number')]
+ if (imgAreaSelectAPIObj === false) return;
+
+ imgAreaSelectAPIObj.cancelSelections(); // notify the imgAreaSelect of the new selection
+ iasSelection = imgAreaSelectAPIObj.createNewSelection(this.get('x1'),
+ this.get('y1'),
+ this.get('x2'),
+ this.get('y2'));
+ imgAreaSelectAPIObj.setOptions({show: true});
+ imgAreaSelectAPIObj.update();
+
+ new_selection = this.clone(); // and create a new Selection.
+ new_selection.set('page_number', page.get('number'));
+ new_selection.set('id', page.get('number') * 100000 + iasSelection.id);
+ new_selection.id = page.get('number') * 100000 + iasSelection.id;
+ this.collection.add(new_selection);
+ /* which causes thumbnails to be created, Download All button to know about these selections. */
+ }
+ }, this));
+ },
+});
+
+Tabula.Options = Backbone.Model.extend({
+ initialize: function(){
+ _.bindAll(this, 'write');
+ this.set('multiselect_mode', localStorage.getItem("tabula-multiselect-mode") !== "false");
+ this.set('extraction_method', null); // don't write this one to localStorage
+ this.set('show_advanced_options', localStorage.getItem("tabula-show-advanced-options") !== "false");
+ this.set('show-directions', localStorage.getItem("tabula-show-directions") !== "false");
+ },
+ write: function(){
+ localStorage.setItem("tabula-multiselect-mode", this.get('multiselect_mode'));
+ localStorage.setItem("tabula-show-advanced-options", this.get('show_advanced_options'));
+ localStorage.setItem("tabula-show-directions", this.get('show-directions'));
+ }
+});
+
+/* What the hell are you doing here, Jeremy?
+ The canonical store of selections now needs to be in Backbone, not in imgareaselect.
+ The UI can listen to the Selections; imgAreaselect creates adds to the collection,
+ causing the thumbnail to be drawn.
+
+ Clearing or repeating is much easier, because we don't have to mess around with the UI.
+ Querying all is likewise easy.
+
+ We could also store extraction option info on the selections, if we want.
+
+ On imgareaselect's _onSelectEnd, add the selection to Selections
+
+ On Selections's remove (or change), find the right imgAreaSelect
+*/
+
+Tabula.Selections = Backbone.Collection.extend({
+ model: Tabula.Selection,
+ url: null, //set on init
+ initialize: function(){
+ this.url = '/pdfs/' + PDF_ID + '/tables.json?_=' + Math.round(+new Date()).toString();
+ _.bindAll(this, 'updateOrCreateByIasId');
+ },
+
+ parse: function(response){
+ // a JSON list of pages, which are each just a list of coords
+ var tables = [];
+ selections = _(response).map(_.bind(function(page_tables, listIndex){
+ var pageIndex = listIndex + 1;
+ var pageView = Tabula.pdf_view.components['document_view'].page_views[pageIndex];
+ var page = pageView.model;
+
+ var original_pdf_width = page.get('width');
+ var original_pdf_height = page.get('height');
+ var pdf_rotation = page.get('rotation');
+
+ pageView.createTables = _.bind(function(){
+ var image_width = pageView.$el.find('img').width();
+ var thumb_height = pageView.$el.find('img').height();
+ var scale = (original_pdf_width / image_width);
+ selections = _(page_tables).map(_.bind(function(tableCoords){
+ var my_x2 = tableCoords[0] + tableCoords[2];
+ var my_y2 = tableCoords[1] + tableCoords[3];
+
+ var iasSelection = pageView.imgAreaSelect.createNewSelection( Math.floor(tableCoords[0] / scale),
+ Math.floor(tableCoords[1] / scale),
+ Math.floor(my_x2 / scale),
+ Math.floor(my_y2 / scale));
+ pageView.imgAreaSelect.setOptions({show: true});
+ pageView.imgAreaSelect.update();
+ if (!iasSelection){
+ return null;
+ }
+
+ // put the selection into the selections collection
+ selection = this.updateOrCreateByIasId(iasSelection, page.get('number'), image_width);
+ return selection;
+ }, this));
+ pageView.imgAreaSelect.setOptions({show: true});
+ return _(selections).select(function(s){ return !!s; });
+ }, this);
+
+ if (pageView.iasAlreadyInited){
+ selections = pageView.createTables();
+ }else{
+ selections = [];
+ }
+ return selections;
+ }, this));
+ return _.flatten(selections);
+ },
+
+ updateOrCreateByIasId: function(iasSelection, pageNumber, imageWidth){
+ var selectionId = pageNumber * 100000 + iasSelection.id;
+ var selection = this.get(selectionId);
+ if(selection){ // if it already exists
+ selection.set(_.omit(iasSelection, 'id'));
+ }else{
+ new_selection_args = _.extend({'page_number': pageNumber,
+ 'imageWidth': imageWidth,
+ 'id': selectionId,
+ 'extractionMethod': Tabula.pdf_view.options.extraction_method,
+ 'pdf_document': this.pdf_document},
+ _.omit(iasSelection, 'id', '$el'))
+ selection = new Tabula.Selection(new_selection_args);
+ this.add(selection);
+ }
+ return selection;
+ }
+
+
+});
+
+Tabula.Query = Backbone.Model.extend({
+ //has selections, data
+ //pertains to DataView
+
+ // on modal exit, destroy this.pdf_view.query
+ // on selection end or download all button, create this.pdf_view.query
+ // in the modal, modify and requery.
+
+ initialize: function(){
+ // should be inited with list_of_coords
+ _.bindAll(this, 'doQuery', 'setExtractionMethod');
+ },
+
+ doQuery: function(options) {
+ this.query_data = {
+ 'coords': JSON.stringify(this.get('list_of_coords')),
+ // ignored by backend 'extraction_method': Tabula.pdf_view.options.get('extraction_method')
+ // because each element of list_of_coords has its own extraction_method key/value
+ }
+
+ this.trigger("tabula:query-start");
+ $.ajax({
+ type: 'POST',
+ url: '/pdf/' + PDF_ID + '/data',
+ data: this.query_data,
+ success: _.bind(function(resp) {
+ this.set('data', resp);
+
+ // this only needs to happen on the first select, when we don't know what the extraction method is yet
+ // (because it's set by the heuristic on the server-side).
+ // TODO: only execute it when one of the list_of_coords has guess or undefined as its extraction_method
+ _(_.zip(this.get('list_of_coords'), resp)).each(function(stuff, i){
+ var coord_set = stuff[0];
+ var resp_item = stuff[1];
+ Tabula.pdf_view.pdf_document.selections.get(coord_set.selection_id).
+ set('extraction_method', resp_item["extraction_method"]);
+ coord_set["extraction_method"] = resp_item["extraction_method"];
+ });
+
+ this.trigger("tabula:query-success");
+
+ if (options !== undefined && _.isFunction(options.success)){
+ Tabula.pdf_view.options.success(resp);
+ }
+
+ }, this),
+ error: _.bind(function(xhr, status, error) {
+ console.log("error!");
+ Tabula.pdf_view.components['data_view'].hideAndTrash();
+ $('#modal-error textarea').html(xhr.responseText);
+ $('#modal-error').modal('show');
+ if (options !== undefined && _.isFunction(options.error))
+ options.error(resp);
+ }, this),
+ });
+ },
+ setExtractionMethod: function(extractionMethod){
+ _(this.get('list_of_coords')).each(function(coord_set){ coord_set['extraction_method'] = extractionMethod});
+ }
+})
+
+Tabula.DataView = Backbone.View.extend({ //one per query object.
+ el: '#data-modal',
+ $loading: $('#loading'),
+ template: _.template($('#templates #modal-footer-template').html().replace(/nestedscript/g, 'script')),
+ events: {
+ 'click .download-dropdown': 'dropDownOrUp',
+ 'click .extraction-method-btn:not(.active)': 'queryWithToggledExtractionMethod',
+ 'click .toggle-advanced-options': 'toggleAdvancedOptions',
+ 'click .download-btn': 'setFormAction',
+ 'hidden': 'handleHidden',
+ //N.B.: Download button (and format-specific download buttons) are an HTML form.
+ //TODO: handle flash clipboard thingy here.
+ },
+ pdf_view: null, //added on create
+ extractionMethod: "guess",
+
+ initialize: function(stuff){
+ _.bindAll(this, 'render', 'renderLoading', 'renderFooter', 'renderTable', 'toggleAdvancedOptions', 'dropDownOrUp', 'queryWithToggledExtractionMethod', 'handleHidden', 'trash', 'hideAndTrash', 'setFormAction');
+ this.pdf_view = stuff.pdf_view;
+ this.listenTo(this.model, 'tabula:query-start', this.render);
+ this.listenTo(this.model, 'tabula:query-success', this.render);
+ this.$modalBody = this.$el.find('.modal-body');
+ },
+
+ // turns out, bootstrap sucks and when the Tooltips are hidden,
+ // they fire the same 'hidden' event as the modal.
+ handleHidden: function(e){
+ if($(e.target).attr('id') == "data-modal" ) {
+ this.trash();
+ }
+ },
+
+ setFormAction: function(e){
+ var formActionUrl = $(e.currentTarget).data('action');
+ this.$el.find('form').attr('action', formActionUrl);
+ },
+
+ hideAndTrash: function(){
+ this.$el.modal('hide');
+ this.trash();
+ },
+
+ trash: function(e){
+ Tabula.pdf_view.trashDataView();
+ return this;
+ },
+
+ renderLoading: function(){
+ $('#switch-method').prop('disabled', true);
+ this.$modalBody.prepend(this.$loading.show());
+ this.$el.find('.modal-body table').css('visibility', 'hidden');
+ this.$modalBody.css('overflow', 'hidden');
+ return this;
+ },
+
+ render: function(){
+ this.$el.modal('show'); //bootstrap stuff
+
+ if(!this.model.get('data')){
+ this.renderLoading();
+ this.renderFooter();
+ }else{
+ this.renderTable();
+ this.renderFooter();
+ }
+
+ this.$el.find('.has-tooltip').tooltip();
+
+ return this;
+ },
+
+ renderFooter: function(){
+ var uniq_extraction_methods = _.uniq(_(this.model.get('list_of_coords')).pluck('extraction_method'));
+
+ templateOptions = {
+ extractionMethodDisabled: _.isNull(this.model.data) || uniq_extraction_methods.length > 1 ? 'disabled="disabled"' : '',
+ pdf_id: PDF_ID,
+ list_of_coords: JSON.stringify(this.model.get('list_of_coords')),
+ copyDisabled: Tabula.pdf_view.flash_borked ? 'disabled="disabled" data-toggle="tooltip" title="'+Tabula.pdf_view.flash_borken_message+'"' : '',
+ }
+
+ //on create, show/hide advanced options area as necessary from this.pdf_view.options
+ if(this.pdf_view.options.get('show_advanced_options')){
+ this.$el.addClass("advanced-options-shown");
+ }
+
+ if (Tabula.pdf_view.flash_borked){
+ this.$el.find('#copy-csv-to-clipboard').addClass('has-tooltip');
+ }
+
+ this.$el.find(".modal-footer-container").html(this.template(templateOptions));
+
+ // this has to happen after the footer is already in the page, for bootstrap reasons.
+ if (uniq_extraction_methods.length == 1){
+ this.$el.find('#' + uniq_extraction_methods[0] + '-method-btn').button('toggle');
+ }
+ },
+
+ renderTable: function(){
+ this.$loading = this.$loading.detach();
+ this.$el.find('.modal-body table').css('visibility', 'visible');
+ this.$modalBody.css('overflow', 'auto');
+
+ var tableHTML = '
';
+ // this.data is a list of responses (because we sent a list of coordinate sets)
+ $.each(_.pluck(this.model.get('data'), 'data'), function(i, rows) {
+ $.each(rows, function(j, row) {
+ tableHTML += '
' + _.pluck(row, 'text').join('
') + '
';
+ });
});
+ tableHTML += '
';
+ this.$modalBody.html(tableHTML);
+
+ if(!Tabula.pdf_view.client){
+ try{
+ Tabula.pdf_view.client = new ZeroClipboard();
+ }catch(e){
+ this.$el.find('#copy-csv-to-clipboard').hide();
+ }
+ }
+ if( !Tabula.pdf_view.flash_borked ){
+ Tabula.pdf_view.client.on( 'ready', _.bind(function(event) {
+ Tabula.pdf_view.client.clip( this.$el.find("#copy-csv-to-clipboard") );
+
+ Tabula.pdf_view.client.on( 'copy', _.bind(function(event) {
+ var clipboard = event.clipboardData;
+ var tableData = this.$el.find('.modal-body table').table2CSV({delivery: null})
+ clipboard.setData( 'text/plain', tableData );
+ }, this) );
+
+ Tabula.pdf_view.client.on( 'aftercopy', function(event) {
+ $('#data-modal #copy-csv-to-clipboard').css('display', 'inline').delay(900).fadeOut('slow');
+ } );
+ }, this) );
+
+ Tabula.pdf_view.client.on( 'error', _.bind(function(event) {
+ //disable all clipboard buttons, add tooltip, event.message
+ Tabula.pdf_view.flash_borked = true;
+ Tabula.pdf_view.flash_borken_message = event.message;
+ this.$el.find('#copy-csv-to-clipboard').addClass('has-tooltip').tooltip();
+ console.log( 'ZeroClipboard error of type "' + event.name + '": ' + event.message );
+ ZeroClipboard.destroy();
+ },this) );
+ }
- $('.has-tooltip').tooltip();
+ return this;
+ },
- Tabula.tour = new Tour(
- {
- storage: false,
- onStart: function(){
- $('a#help-start').text("Close Help");
- },
- onEnd: function(){
- $('a#help-start').text("Help");
+ dropDownOrUp: function(e){
+ var $el = $(e.currentTarget);
+ $ul = $el.parent().find('ul');
+
+ window.setTimeout(function(){ // TODO: if we upgrade to bootstrap 3.0
+ // we don't need this gross timeout and can, instead,
+ // listen for the `dropdown's shown.bs.dropdown` event
+ if(!isElementInViewport($ul)){
+ $el.addClass('dropup');
+ $ul.addClass('bottom-up');
+ }
+ }, 100);
+ },
+
+ toggleAdvancedOptions: function(e){
+ this.pdf_view.options.set('show_advanced_options', !this.pdf_view.options.get('show_advanced_options'));
+ if(this.pdf_view.options.get('show_advanced_options')){
+ this.$el.addClass("advanced-options-shown");
+ }else{
+ this.$el.removeClass("advanced-options-shown");
}
- });
-
- Tabula.tour.addSteps([
- {
- content: "Click and drag to select each table in your document. Once you've selected it, a window to preview your data will appear, along with options to download it as a spreadsheet.",
- element: ".page-image#page-1",
- title: "Select Tables",
- placement: 'right'
- },
- {
- element: "#all-data",
- title: "Download Data",
- content: "When you've selected all of the tables in your PDF, click this button to preview the data from all of the selections and download it.",
- placement: 'left'
- },
- {
- element: "#should-preview-data-checkbox",
- title: "Preview Data Automatically?",
- content: "After you select each table on a page, a data preview window will appear automatically. If you want to select multiple tables without interruption, uncheck this box to suppress the preview window.",
- placement: 'left'
- },
- {
- element: "#thumb-page-2",
- title: "Page Shortcuts",
- content: "Click a thumbnail to skip directly to that page.",
- placement: 'right',
- parent: 'body'
+ return false;
+ },
+
+
+ queryWithToggledExtractionMethod: function(e){
+ this.model.set('data', null);
+ var extractionMethod = $(e.currentTarget).data('method');
+ this.pdf_view.options.set('extraction_method', extractionMethod);
+ Tabula.pdf_view.query.setExtractionMethod(extractionMethod);
+ Tabula.pdf_view.query.doQuery();
+ },
+});
+
+Tabula.DocumentView = Backbone.View.extend({ //only one
+ events: {
+ 'click button.close#directions' : 'closeDirections',
+ },
+ pdf_view: null, //added on create
+ page_views: {},
+
+ /* when the Directions area is closed, the pages themselves move up, because they're just static positioned.
+ * The selections on those images, though, do not move up, and need to be moved up separately, since they're fixed.
+ */
+ closeDirections: function(){
+ this.pdf_view.options.set('show-directions', false);
+
+ var directionsRow = $('#directionsRow')
+ var height = directionsRow.height()
+ $('div.imgareaselect-box').each(function(){
+ $(this).offset({top: $(this).offset()["top"] - height });
+ });
+ directionsRow.remove();
+ },
+
+ initialize: function(stuff){
+ _.bindAll(this, 'render', 'removePage');
+ this.pdf_view = stuff.pdf_view;
+ this.listenTo(this.collection, 'remove', this.removePage)
+ },
+
+ removePage: function(pageModel){
+ var page_view = this.page_views[pageModel.get('number')];
+
+ page_view.$el.fadeOut(200, function(){
+
+ // move all the stuff for the following pages' imgAreaSelect objects up.
+ deleted_page_height = page_view.$el.height();
+ deleted_page_top = page_view.$el.offset()["top"];
+
+ $('div.imgareaselect').each(function(){
+ if( $(this).offset()["top"] > (deleted_page_top + deleted_page_height) ){
+ $(this).offset({top: $(this).offset()["top"] - deleted_page_height });
+ }
+ });
+
+ //TODO: edit imgAreaSelect to:
+ // (a) not be position fixed (so I don't have to move their location manualy)
+ // e.g. something like _(Tabula.pdf_view.imgAreaSelects).each(function(ias){ ias.adjust(); });
+ // (b) listen on document, no matter how many exist on the page.
+
+ page_view.imgAreaSelect.remove();
+ page_view.remove()
+ });
+ },
+
+ render: function(){
+ if(!this.pdf_view.options.get('show-directions')){
+ this.$el.find('#directionsRow').remove();
}
- ]);
+ return this;
+ },
});
-//make the "follow you around bar" actually follow you around. ("sticky nav")
-$(document).ready(function() {
- $('.followyouaroundbar').affix({top: 70 });
+Tabula.PageView = Backbone.View.extend({ // one per page of the PDF
+ document_view: null, //added on create
+ className: 'row pdf-page',
+ iasAlreadyInited: false,
+ id: function(){
+ return 'page-' + this.model.get('number');
+ },
+ template: _.template($('#templates #page-template').html().replace(/nestedscript/g, 'script')) ,
+ 'events': {
+ 'click i.rotate-left i.rotate-right': 'rotate_page',
+ },
+
+ initialize: function(stuff){
+ this.pdf_view = stuff.pdf_view;
+ _.bindAll(this, 'createImgareaselect', 'rotate_page', 'createTables',
+ '_onSelectStart', '_onSelectChange', '_onSelectEnd', '_onSelectCancel', 'render');
+ },
+
+ render: function(){
+ this.$el.html(this.template({
+ 'number': this.model.get('number'),
+ 'image_url': this.model.get('image_url')
+ }));
+ this.$el.find('img').attr('data-page', this.model.get('number'))
+ .attr('data-original-width', this.model.get('width'))
+ .attr('data-original-height', this.model.get('height'))
+ .attr('data-rotation', this.model.get('rotation'));
+ if(this.model.number == 1){
+ this.$el.find('img').attr('data-position', "right")
+ .attr('data-intro', "Click and drag to select each table in your document. Once you've selected it, a window to preview your data will appear, along with options to download it as a spreadsheet.");
+ }
+
+
+ Tabula.pdf_view.imgAreaSelects[this.model.get('number')] = this.createImgareaselect() ;
+ return this;
+ },
+
+ createImgareaselect: function(){
+ if (this.model.get('deleted')) {
+ return false;
+ }
+
+ this.$image = this.$el.find('img');
+ var ias = this.$image.imgAreaSelect({
+ handles: true,
+ instance: true,
+ allowOverlaps: false,
+ show: true,
+ multipleSelections: true,
+
+ onSelectStart: this._onSelectStart,
+ onSelectChange: this._onSelectChange,
+ onSelectEnd: this._onSelectEnd,
+ onSelectCancel: this._onSelectCancel,
+ onInit: _.bind(function(){ this.createTables() }, this)
+ });
+ this.imgAreaSelect = ias;
+ return ias;
+ },
+
+ createTables: function(asfd){
+ this.iasAlreadyInited = true;
+ },
+
+ _onSelectStart: function(img, iasSelection) {
+ Tabula.pdf_view.pdf_document.selections.updateOrCreateByIasId(iasSelection, this.model.get('number'), this.$image.width());
+ },
+
+ _onSelectChange: function(img, iasSelection) {
+ Tabula.pdf_view.pdf_document.selections.updateOrCreateByIasId(iasSelection, this.model.get('number'), this.$image.width());
+
+ // This is for moving the repeat lassos button, I think. -Jeremy 7/31/14
+ var b;
+ var but_id = $(img).attr('id') + '-' + iasSelection.id;
+ if (b = $('button#' + but_id)) {
+ var img_pos = $(img).offset();
+ $(b)
+ .css({
+ top: img_pos.top + iasSelection.y1 + iasSelection.height - $('button#' + but_id).height() * 1.5,
+ left: img_pos.left + iasSelection.x1 + iasSelection.width + 5
+ })
+ .data('selection', iasSelection);
+ }
+ },
+
+ _onSelectEnd: function(img, iasSelection) {
+ var selection = Tabula.pdf_view.pdf_document.selections.updateOrCreateByIasId(iasSelection, this.model.get('number'), this.$image.width());
+
+ // deal with invalid/too-small iasSelections somehow (including thumbnails)
+ if (iasSelection.width == 0 && iasSelection.height == 0) {
+ $('#thumb-' + $(img).attr('id') + ' #iasSelection-show-' + iasSelection.id).css('display', 'none');
+ selection.destroy();
+ }
+
+ if(this.model != this.model.collection.last()){ // if this is not the last page
+ var but_id = this.model.get('number') + '-' + iasSelection.id; //create a "Repeat this Selection" button
+ var button = $('');
+ button.data("selectionId", (this.model.get('number') * 100000) + iasSelection.id )
+ iasSelection.$el.append(button);
+ }
+
+ if(!Tabula.pdf_view.options.get('multiselect_mode')){
+ selection.queryForData();
+ }
+ Tabula.pdf_view.components['control_panel'].render(); // deals with buttons that need blurred out if there's zero selections, etc.
+ },
+
+ // iasSelection
+ _onSelectCancel: function(img, iasSelection) {
+ // remove repeat lassos button
+ var but_id = $(img).attr('id') + '-' + iasSelection.id;
+ $('button#' + but_id).remove();
+
+ // find and remove the canceled selection from the collection of selections. (triggering remove events).
+ var selectionId = (this.model.get('number') * 100000) + iasSelection.id;
+ var selection = Tabula.pdf_view.pdf_document.selections.get(selectionId);
+ removed_selection = Tabula.pdf_view.pdf_document.selections.remove(selection);
+
+ Tabula.pdf_view.components['control_panel'].render(); // deal with buttons that need blurred out if there's zero selections, etc.
+ },
+
+ rotate_page: function(t) {
+ alert('not implemented');
+ },
+});
+
+
+/* I'm not sure having a SelectionView makes sense. But,
+ * TODO: something needs to manage the repeat lasso button other than the body element.
+ */
+
+Tabula.ControlPanelView = Backbone.View.extend({ // only one
+ events: {
+ 'click #should-preview-data-checkbox' : 'updateShouldPreviewDataAutomaticallyButton',
+ 'click #clear-all-selections': 'clear_all_selection',
+ 'click #restore-detected-tables': 'restore_detected_tables',
+ 'click #all-data': 'query_all_data',
+ 'click #repeat-lassos': 'repeatLassos',
+ },
+ className: 'followyouaroundbar',
+
+ template: _.template($('#templates #control-panel-template').html().replace(/nestedscript/g, 'script')),
+
+ shouldPreviewDataAutomatically: !$('#should-preview-data-checkbox').is(':checked'),
+
+ updateShouldPreviewDataAutomaticallyButton: function(){
+ this.pdf_view.options.set('multiselect_mode', !$('#should-preview-data-checkbox').is(':checked'))
+ this.render();
+ },
+
+ /* in case there's a PDF with a complex format that's repeated on multiple pages */
+ repeatFirstPageLassos: function(){
+ alert('not yet implemented');
+ return;
+ /* TODO: write this */
+ },
+
+ clear_all_selection: function(){
+ _(this.pdf_view.imgAreaSelects).each(function(imgAreaSelectAPIObj){
+ if (imgAreaSelectAPIObj === false) return;
+ imgAreaSelectAPIObj.cancelSelections();
+ });
+ },
+
+ restore_detected_tables: function(){
+ this.pdf_view.pdf_document.selections.reset([]);
+ this.pdf_view.pdf_document.selections.fetch();
+ },
+
+ initialize: function(stuff){
+ this.pdf_view = stuff.pdf_view
+ _.bindAll(this, 'updateShouldPreviewDataAutomaticallyButton', 'query_all_data', 'render');
+ },
+
+ query_all_data : function(){
+ var list_of_all_coords = Tabula.pdf_view.pdf_document.selections.invoke("toCoords");
+
+ //TODO: figure out how to handle extraction methods when there are multiple selections
+ // should it be set globally, or per selection?
+ // actually, how to handle extraction method is a bit of an open question.
+ // should we support in the UI extraction methods per selection?
+ // if so, what does the modal show if its showing results from more than one selection?
+ // maybe it only shows them if they match?
+ // or not at all ever?
+ // but then we need to make it clearer in the UI that you are "editing" a selection.
+ // which will require different reactions with multiselect mode:
+ // when you finish a query, then still pop up its data.
+ // when you click or move an already-selected query, then you're "editing" it?
+ // hmm.
+ Tabula.pdf_view.query = new Tabula.Query({list_of_coords: list_of_all_coords, extraction_method: 'guess'});
+ Tabula.pdf_view.createDataView();
+ Tabula.pdf_view.query.doQuery();
+ },
+
+ render: function(){
+ // makes the "follow you around bar" actually follow you around. ("sticky nav")
+ $('.followyouaroundbar').affix({top: 70 });
+
+ var numOfSelectionsOnPage = this.pdf_view.totalSelections();
+ this.$el.html(this.template({
+ 'if_multiselect_checked': this.pdf_view.options.get('multiselect_mode') ? '' : 'checked="checked"',
+ 'disable_clear_all_selections': numOfSelectionsOnPage <= 0 ? 'disabled="disabled"' : '' ,
+ 'disable_download_all': numOfSelectionsOnPage <= 0 ? 'disabled="disabled"' : '',
+ 'show_restore_detected_tables': (this.pdf_view.hasPredetectedTables && numOfSelectionsOnPage <= 0) ? '' : 'display: none;',
+ }));
+
+ return this;
+ },
+});
+
+Tabula.SidebarView = Backbone.View.extend({ // only one
+ tagName: 'ul',
+ className: 'thumbnail-list',
+ thumbnail_views: {},
+ pdf_view: null, // defined on initialize
+ initialize: function(stuff){
+ this.pdf_view = stuff.pdf_view;
+ _.bindAll(this, 'addSelectionThumbnail', 'removeSelectionThumbnail', 'changeSelectionThumbnail', 'removeThumbnail')
+
+ this.listenTo(this.collection, 'remove', this.removeThumbnail)
+
+ this.listenTo(this.pdf_view.pdf_document.selections, 'all', this.render);
+ this.listenTo(this.pdf_view.pdf_document.selections, 'add', this.addSelectionThumbnail); // render a thumbnail selection
+ this.listenTo(this.pdf_view.pdf_document.selections, 'change', this.changeSelectionThumbnail); // render a thumbnail selection
+ this.listenTo(this.pdf_view.pdf_document.selections, 'remove', this.removeSelectionThumbnail); // remove a thumbnail selection
+ },
+ addSelectionThumbnail: function (new_selection){
+ this.thumbnail_views[new_selection.get('page_number')].createSelectionThumbnail(new_selection)
+ },
+ changeSelectionThumbnail: function (selection){
+ this.thumbnail_views[selection.get('page_number')].changeSelectionThumbnail(selection)
+ },
+ removeSelectionThumbnail: function (selection){
+ this.thumbnail_views[selection.get('page_number')].removeSelectionThumbnail(selection)
+ },
+
+ removeThumbnail: function(pageModel){
+ var thumbnail_view = this.thumbnail_views[pageModel.get('number')];
+ thumbnail_view.$el.fadeOut(200, function(){ thumbnail_view.remove() });
+ },
});
+Tabula.ThumbnailView = Backbone.View.extend({ // one per page
+ 'events': {
+ // on load, create an empty div with class 'selection-show' to be the selection thumbnail.
+ 'load .thumbnail-list li img': function() { $(this).after($('', { class: 'selection-show'})); },
+ 'click i.delete-page': 'delete_page',
+ },
+ tagName: 'li',
+ className: "thumbnail pdf-page",
+ id: function(){
+ return 'thumb-page-' + this.model.get('number');
+ },
+
+ // initialize: function(){
+ // },
+ template: _.template($('#templates #thumbnail-template').html().replace(/nestedscript/g, 'script')),
+
+ initialize: function(){
+ _.bindAll(this, 'render', 'createSelectionThumbnail', 'changeSelectionThumbnail', 'removeSelectionThumbnail');
+ },
+
+ delete_page: function(){
+ if (!confirm('Delete page ' + this.model.get('number') + '?')) return;
+ Tabula.pdf_view.pdf_document.page_collection.remove( this.model );
+ },
+
+ render: function(){
+ this.$el.html(this.template({
+ 'number': this.model.get('number'),
+ 'image_url': this.model.get('image_url')
+ }));
+
+ if(this.model.get('number') == 1){
+ this.$el.find('img').attr('data-position', "right")
+ .attr('data-intro', "Click a thumbnail to skip directly to that page.");
+ }
+
+ // stash some selectors (which don't exist at init)
+ this.$img = this.$el.find('img');
+ this.img = this.$img[0];
+
+ return this;
+ },
+
+ createSelectionThumbnail: function(selection) {
+ var $sshow = $('').css('display', 'block');
+ this.$el.append( $sshow );
+ this.changeSelectionThumbnail(selection);
+ },
+
+ changeSelectionThumbnail: function(selection){
+ var $sshow = this.$el.find('#selection-show-' + selection.cid);
+ var thumbScale = this.$img.width() / selection.get('imageWidth');
+
+ $sshow.css('top', selection.get('y1') * thumbScale + 'px')
+ .css('left', selection.get('x1') * thumbScale + 'px')
+ .css('width', ((selection.get('x2') - selection.get('x1')) * thumbScale) + 'px')
+ .css('height', ((selection.get('y2') - selection.get('y1')) * thumbScale) + 'px');
+ },
+
+ removeSelectionThumbnail: function(selection){
+ var $sshow = this.$el.find('#selection-show-' + selection.cid);
+ $sshow.remove();
+ }
+})
+
Tabula.PDFView = Backbone.View.extend({
- el : 'body',
- events : {
- 'click button.close#directions' : 'moveSelectionsUp',
- 'click a.tooltip-modal': 'tooltip', //$('a.tooltip-modal').tooltip();
- 'hide #data-modal' : function(){ clip.unglue('#copy-csv-to-clipboard'); },
- 'load .thumbnail-list li img': function() { $(this).after($('', { class: 'selection-show'})); },
- 'click i.delete-page': 'deletePage',
- 'click i.rotate-left i.rotate-right': 'rotatePage',
- 'click button.repeat-lassos': 'repeat_lassos',
+ el : '#tabula-app',
+ events : {
+ 'click a.tooltip-modal': 'tooltip',
'click a#help-start': function(){ Tabula.tour.ended ? Tabula.tour.restart(true) : Tabula.tour.start(true); },
-
- //events for buttons on the follow-you-around bar.
- 'click #should-preview-data-checkbox' : 'updateShouldPreviewDataAutomaticallyButton',
- 'click #clear-all-selections': 'clear_all_selection',
- 'click #restore-detected-tables': 'restore_detected_tables',
- 'click #repeat-lassos': 'repeat_lassos',
- 'click #all-data': 'query_all_data',
- 'click .extraction-method-btn:not(.active)': 'queryWithToggledExtractionMethod',
- 'click .toggle-advanced-options': 'toggleAdvancedOptionsShown',
- 'click .download-dropdown': 'dropDownOrUp'
},
- extractionMethod: "guess",
- $loading: $('#loading'),
- PDF_ID: window.location.pathname.split('/')[2],
colors: ['#f00', '#0f0', '#00f', '#ffff00', '#FF00FF'],
- noModalAfterSelect: !$('#should-preview-data-checkbox').is(':checked'),
lastQuery: [{}],
- lastSelection: undefined,
pageCount: undefined,
+ components: {},
+ imgAreaSelects: {},
+
+ hasPredetectedTables: false,
+ global_options: null,
initialize: function(){
- _.bindAll(this, 'render', 'createImgareaselects', 'getTablesJson', 'total_selections',
- 'toggleClearAllAndRestorePredetectedTablesButtons', 'updateShouldPreviewDataAutomaticallyButton',
- 'query_all_data', 'redoQuery', 'toggleAdvancedOptionsShown');
- this.pageCount = $('img.page-image').length;
- this.setAdvancedOptionsShown();
- this.render();
- this.updateExtractionMethodButton();
+ _.bindAll(this, 'render', 'addOne', 'addAll', 'totalSelections',
+ 'createDataView','trashDataView');
+
+ this.pdf_document = new Tabula.Document({
+ pdf_id: PDF_ID,
+ });
+
+ this.options = new Tabula.Options();
+ this.listenTo(this.options, 'change', this.options.write);
+
+ this.createTour();
+
+ this.listenTo(this.pdf_document.page_collection, 'all', this.render);
+ this.listenTo(this.pdf_document.page_collection, 'add', this.addOne);
+ this.listenTo(this.pdf_document.page_collection, 'reset', this.addAll);
+ this.listenTo(this.pdf_document.page_collection, 'remove', this.removePage);
+
+ this.components['document_view'] = new Tabula.DocumentView({el: '#main-container' , pdf_view: this, collection: this.pdf_document.page_collection}); //creates page_views
+ this.components['control_panel'] = new Tabula.ControlPanelView({pdf_view: this});
+ this.components['sidebar_view'] = new Tabula.SidebarView({pdf_view: this, collection: this.pdf_document.page_collection});
+
+ this.pdf_document.page_collection.fetch({
+ success: _.bind(function(){
+
+ if(!this.pdf_document.page_collection.hasText()){
+ console.log("Scanned PDF detected")
+ alert("Is this PDF scanned? It has no embedded text that Tabula can understand. Unfortunately, this means Tabula won't work. Sorry. ")
+ }
+
+ this.pdf_document.selections.fetch({
+ success: _.bind(function(){
+ this.hasPredetectedTables = true;
+ }, this)
+ });
+ }, this),
+ error: _.bind(function(){
+ console.log('404'); //TODO: make this a real 404.
+ }),
+ });
+
+ $('body'). // imgareaselect selections are fixed positioned in CSS, just attached to the body in DOM
+ on("click", ".imgareaselect-box .repeat-lassos", function(e){
+ var selectionId = $(e.currentTarget).data('selectionId');
+ var selection = Tabula.pdf_view.pdf_document.selections.get(selectionId);
+ selection.repeatLassos();
+ });
+
+
},
- render : function(){
- query_parameters = {};
- this.getTablesJson();
- return this;
+ removePage: function(removedPageModel){
+ $.post('/pdf/' + PDF_ID + '/page/' + removedPageModel.get('number'),
+ { _method: 'delete' },
+ function () {
+ Tabula.pdf_view.pageCount -= 1;
+ });
+
+ // removing the views is handled by the views themselves.
+
+ //remove selections
+ var selections = this.pdf_document.selections.where({page_number: removedPageModel.get('number')});
+ this.pdf_document.selections.remove(selections);
+ },
+
+ createDataView: function(){
+ this.components['data_view'] = new Tabula.DataView({pdf_view: this, model: Tabula.pdf_view.query});
},
+ trashDataView: function(){
+ // the modal HTML stays on the page, so undelegate the events we bound to it via Backbone
+ // (but keeping the Bootstrap events)
+ this.components['data_view'].undelegateEvents();
+ this.components['data_view'] = null;
+ },
- queryWithToggledExtractionMethod: function(e){
- // console.log("before", this.extractionMethod);
- this.extractionMethod = this.getOppositeExtractionMethod();
- // console.log("after", this.extractionMethod);
- this.updateExtractionMethodButton();
+ addOne: function(page) {
+ if(page.get('deleted')){
+ return;
+ }
+ var page_view = new Tabula.PageView({model: page, collection: this.pdf_document.page_collection});
+ var thumbnail_view = new Tabula.ThumbnailView({model: page, collection: this.pdf_document.page_collection})
+ this.components['document_view'].page_views[ page.get('number') ] = page_view;
+ this.components['sidebar_view'].thumbnail_views[ page.get('number') ] = thumbnail_view;
+ this.components['document_view'].$el.append(page_view.render().el);
+ this.components['sidebar_view'].$el.append(thumbnail_view.render().el);
+ },
- this.redoQuery();
+ addAll: function() {
+ this.pdf_document.page_collection.each(this.addOne, this);
},
+ totalSelections: function(){
+ if(_.isUndefined(this.imgAreaSelects)){
+ return 0;
+ }
- rotatePage: function(t) {
- alert('not implemented');
+ return _.reduce(this.imgAreaSelects, function(memo, imgAreaSelect, pageNum){
+ if(imgAreaSelect){
+ return memo + imgAreaSelect.getSelections().length;
+ }else{
+ return memo;
+ }
+ }, 0);
},
- deletePage: function(t) {
- var page_thumbnail = $(t.target).parent().parent();
- var page_number = page_thumbnail.data('page').split('-')[1];
- var that = this;
- if (!confirm('Delete page ' + page_number + '?')) return;
- $.post('/pdf/' + this.PDF_ID + '/page/' + page_number,
- { _method: 'delete' },
- function () {
-
- // delete the deleted page's imgAreaSelect object
- imgAreaSelects[page_number-1].remove();
- delete imgAreaSelects[page_number-1];
-
- // move all the stuff for the following pages' imgAreaSelect objects up.
- deleted_page_height = $('img.page-image#page-' + page_number).height();
- deleted_page_top = $('img.page-image#page-' + page_number).offset()["top"];
-
- $('img.page-image#page-' + page_number)
- .fadeOut(200,
- function() { $(this).remove(); });
- page_thumbnail
- .fadeOut(200,
- function() { $(this).remove(); });
-
- $('div.imgareaselect').each(function(){
- if( $(this).offset()["top"] > (deleted_page_top + deleted_page_height) ){
- $(this).offset({top: $(this).offset()["top"] - deleted_page_height });
- }
- });
- that.pageCount -= 1;
- });
+ render : function(){
+ this.components['document_view'].render();
+ $('#control-panel-container').append(this.components['control_panel'].render().el);
+ $('.sidebar-nav.well').append(this.components['sidebar_view'].render().el);
+
+ $('.has-tooltip').tooltip();
+
+ this.pageCount = this.pdf_document.page_collection.size();
+ return this;
},
- redoQuery: function(options) {
- //TODO: stash lastCoords, rather than stashing lastQuery and then parsing it.
- this.doQuery(this.PDF_ID,
- JSON.parse(this.lastQuery["coords"]),
- options);
+ createTour: function(){
+ Tabula.tour = new Tour(
+ {
+ storage: false,
+ onStart: function(){
+ $('a#help-start').text("Close Help");
+ },
+ onEnd: function(){
+ $('a#help-start').text("Help");
+ }
+ });
+
+ Tabula.tour.addSteps([
+ {
+ content: "Click and drag to select each table in your document. Once you've selected it, a window to preview your data will appear, along with options to download it as a spreadsheet.",
+ element: "#page-div-1 .page-image",
+ title: "Select Tables",
+ placement: 'right'
+ },
+ {
+ element: "#all-data",
+ title: "Download Data",
+ content: "When you've selected all of the tables in your PDF, click this button to preview the data from all of the selections and download it.",
+ placement: 'left'
+ },
+ {
+ element: "#should-preview-data-checkbox",
+ title: "Preview Data Automatically?",
+ content: "After you select each table on a page, a data preview window will appear automatically. If you want to select multiple tables without interruption, uncheck this box to suppress the preview window.",
+ placement: 'left'
+ },
+ {
+ element: "#thumb-page-2",
+ title: "Page Shortcuts",
+ content: "Click a thumbnail to skip directly to that page.",
+ placement: 'right',
+ parent: 'body'
+ }
+ ]);
},
debugRulings: function(image, render, clean, show_intersections) {
@@ -173,21 +1016,21 @@ Tabula.PDFView = Backbone.View.extend({
$('body').append(newCanvas);
var pdf_rotation = parseInt($(image).data('rotation'));
- var pdf_width = parseInt($(image).data('original-width'));
- var pdf_height = parseInt($(image).data('original-height'));
+ var original_pdf_width = parseInt($(image).data('original-width'));
+ var original_pdf_height = parseInt($(image).data('original-height'));
var thumb_width = $(image).width();
- var scale = thumb_width / (Math.abs(pdf_rotation) == 90 ? pdf_height : pdf_width);
+ var scale = thumb_width / (Math.abs(pdf_rotation) == 90 ? original_pdf_height : original_pdf_width);
var lq = $.extend(this.lastQuery,
{
- pdf_page_width: pdf_width,
+ pdf_page_width: original_pdf_width,
render_page: render == true,
clean_rulings: clean == true,
show_intersections: show_intersections == true
});
- $.get('/debug/' + this.PDF_ID + '/rulings',
+ $.get('/debug/' + PDF_ID + '/rulings',
lq,
_.bind(function(data) {
$.each(data.rulings, _.bind(function(i, ruling) {
@@ -211,7 +1054,7 @@ Tabula.PDFView = Backbone.View.extend({
},
_debugRectangularShapes: function(image, url) {
- image = $(image);
+ image = $(image);
var imagePos = image.offset();
var newCanvas = $('',{'class':'debug-canvas'})
.attr('width', image.width())
@@ -222,11 +1065,11 @@ Tabula.PDFView = Backbone.View.extend({
var thumb_width = $(image).width();
var thumb_height = $(image).height();
- var pdf_width = parseInt($(image).data('original-width'));
- var pdf_height = parseInt($(image).data('original-height'));
+ var original_pdf_width = parseInt($(image).data('original-width'));
+ var original_pdf_height = parseInt($(image).data('original-height'));
var pdf_rotation = parseInt($(image).data('rotation'));
- var scale = thumb_width / (Math.abs(pdf_rotation) == 90 ? pdf_height : pdf_width);
+ var scale = thumb_width / (Math.abs(pdf_rotation) == 90 ? original_pdf_height : original_pdf_width);
$.get(url,
this.lastQuery,
@@ -246,11 +1089,11 @@ Tabula.PDFView = Backbone.View.extend({
},
debugCharacters: function(image) {
- return this._debugRectangularShapes(image, '/debug/' + this.PDF_ID + '/characters');
+ return this._debugRectangularShapes(image, '/debug/' + PDF_ID + '/characters');
},
debugClippingPaths: function(image) {
- return this._debugRectangularShapes(image, '/debug/' + this.PDF_ID + '/clipping_paths');
+ return this._debugRectangularShapes(image, '/debug/' + PDF_ID + '/clipping_paths');
},
debugColumns: function(image) {
@@ -265,24 +1108,23 @@ Tabula.PDFView = Backbone.View.extend({
var thumb_width = $(image).width();
var thumb_height = $(image).height();
- var pdf_width = parseInt($(image).data('original-width'));
- var pdf_height = parseInt($(image).data('original-height'));
+ var original_pdf_width = parseInt($(image).data('original-width'));
+ var original_pdf_height = parseInt($(image).data('original-height'));
var pdf_rotation = parseInt($(image).data('rotation'));
- var scale = thumb_width / (Math.abs(pdf_rotation) == 90 ? pdf_height : pdf_width);
+ var scale = thumb_width / (Math.abs(pdf_rotation) == 90 ? original_pdf_height : original_pdf_width);
- var coords = JSON.parse(this.lastQuery.coords);
+ var list_of_coords = JSON.parse(this.lastQuery.coords);
- this.redoQuery({
+ Tabula.pdf_view.query.doQuery({
success: _.bind(function(data) {
var colors = this.colors;
- console.log(coords);
$.each(data[0].vertical_separators, function(i, vert) {
newCanvas.drawLine({
strokeStyle: colors[i % colors.length],
strokeWidth: 1,
- x1: vert * scale, y1: coords[0].y1 * scale,
- x2: vert * scale, y2: coords[0].y2 * scale
+ x1: vert * scale, y1: list_of_coords[0].y1 * scale,
+ x2: vert * scale, y2: list_of_coords[0].y2 * scale
});
});
}, this)});
@@ -295,431 +1137,10 @@ Tabula.PDFView = Backbone.View.extend({
},
debugTextChunks: function(image) {
- return this._debugRectangularShapes(image, '/debug/' + this.PDF_ID + '/text_chunks');
- },
-
- /* functions for the follow-you-around bar */
- total_selections: function(){
- return _.reduce(imgAreaSelects, function(memo, s){
- if(s){
- return memo + s.getSelections().length;
- }else{
- return memo;
- }
- }, 0);
- },
- toggleClearAllAndRestorePredetectedTablesButtons: function(numOfSelectionsOnPage){
- // if tables weren't autodetected, don't tease the user with an autodetect button that won't work.
- if(!_(tableGuesses).isEmpty()){
- if(numOfSelectionsOnPage <= 0){
- $("#clear-all-selections").hide();
- $("#restore-detected-tables").show();
- }else{
- $("#clear-all-selections").show();
- $("#restore-detected-tables").hide();
- }
- }
- },
- clear_all_selection: function(){
- _(imgAreaSelects).each(function(imgAreaSelectAPIObj){
- if (imgAreaSelectAPIObj === false) return;
- imgAreaSelectAPIObj.cancelSelections();
- });
- },
-
- restore_detected_tables: function(){
- for(var imageIndex=0; imageIndex < imgAreaSelects.length; imageIndex++){
- var pageIndex = imageIndex + 1;
- this.drawDetectedTables( $('img#page-' + pageIndex), tableGuesses );
- }
- this.toggleClearAllAndRestorePredetectedTablesButtons(this.total_selections());
- },
-
- toggleDownloadAllAndClearButtons: function() {
- if (this.total_selections() > 0) {
- $('#all-data, #clear-all-selections').removeAttr('disabled');
- }
- else {
- $('#all-data, #clear-all-selections').attr('disabled', 'disabled');
- }
- },
-
- repeat_lassos: function(e) {
- var page_idx = parseInt($(e.currentTarget).attr('id').split('-')[1]);
- var selection_to_clone = $(e.currentTarget).data('selection');
-
- $(e.currentTarget).fadeOut(500, function() { $(this).remove(); });
-
- $('#should-preview-data-checkbox').prop('checked', false);
- this.updateShouldPreviewDataAutomaticallyButton();
-
- imgAreaSelects.slice(page_idx).forEach(function(imgAreaSelectAPIObj) {
- if (imgAreaSelectAPIObj === false) return;
- imgAreaSelectAPIObj.cancelSelections();
- imgAreaSelectAPIObj.createNewSelection(selection_to_clone.x1, selection_to_clone.y1,
- selection_to_clone.x2, selection_to_clone.y2);
- imgAreaSelectAPIObj.setOptions({show: true});
- imgAreaSelectAPIObj.update();
- this.showSelectionThumbnail(imgAreaSelectAPIObj.getImg(),
- selection_to_clone);
- }, this);
+ return this._debugRectangularShapes(image, '/debug/' + PDF_ID + '/text_chunks');
},
-
- query_all_data : function(){
- all_coords = [];
- imgAreaSelects.forEach(function(imgAreaSelectAPIObj){
-
- if (imgAreaSelectAPIObj === false) return;
-
- var thumb_width = imgAreaSelectAPIObj.getImg().width();
- var thumb_height = imgAreaSelectAPIObj.getImg().height();
-
- var pdf_width = parseInt(imgAreaSelectAPIObj.getImg().data('original-width'));
- var pdf_height = parseInt(imgAreaSelectAPIObj.getImg().data('original-height'));
- var pdf_rotation = parseInt(imgAreaSelectAPIObj.getImg().data('rotation'));
-
- var scale = (Math.abs(pdf_rotation) == 90 ? pdf_height : pdf_width) / thumb_width;
-
- imgAreaSelectAPIObj.getSelections().forEach(function(selection){
- new_coord = {
- x1: selection.x1 * scale,
- x2: selection.x2 * scale,
- y1: selection.y1 * scale,
- y2: selection.y2 * scale,
- page: imgAreaSelectAPIObj.getImg().data('page')
- }
- all_coords.push(new_coord);
- });
- });
- this.doQuery(this.PDF_ID, all_coords);
- },
-
- doQuery: function(pdf_id, coords, options) {
- this.lastQuery = {
- coords: JSON.stringify(coords) ,
- 'extraction_method': this.extractionMethod
- };
-
- $('#data-modal').modal();
- this.setAdvancedOptionsShown();
- $('#switch-method').prop('disabled', true);
- $('#data-modal .modal-body').prepend(this.$loading.show());
- $('#data-modal .modal-body table').css('visibility', 'hidden');
- $('#data-modal .modal-body').css('overflow', 'hidden');
-
- $.ajax({
- type: 'POST',
- url: '/pdf/' + pdf_id + '/data',
- data: this.lastQuery,
- success: _.bind(function(resp) {
- this.extractionMethod = resp[0]["extraction_method"];
- this.updateExtractionMethodButton();
-
- //$('#data-modal').find('#loading').hide();
- this.$loading = this.$loading.detach();
- console.log("resp", resp);
- console.log("Extraction method: ", this.extractionMethod);
-
- this.$loading = this.$loading.detach();
- $('#switch-method').prop('disabled', false);
- $('#data-modal .modal-body table').css('visibility', 'visible');
- $('#data-modal .modal-body').css('overflow', 'auto');
-
-
- var tableHTML = '