From a89b2fa702217ca6918868c827869bffff59dfee Mon Sep 17 00:00:00 2001 From: Romain Courteaud Date: Tue, 17 Dec 2019 13:12:25 +0000 Subject: [PATCH 01/42] [erp5_web_renderjs_ui] Add domsugar lib --- .../web_page_module/rjs_domsugar_js.js | 136 ++++++++ .../web_page_module/rjs_domsugar_js.xml | 330 ++++++++++++++++++ 2 files changed, 466 insertions(+) create mode 100644 bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_domsugar_js.js create mode 100644 bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_domsugar_js.xml diff --git a/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_domsugar_js.js b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_domsugar_js.js new file mode 100644 index 00000000000..6a2d4bae583 --- /dev/null +++ b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_domsugar_js.js @@ -0,0 +1,136 @@ +// DOM sugar +// ================================================================== +// Modified version of "Sugared DOM" https://gist.github.com/jacobrask/3524145 +// +// Usage +// ------------------------------------------------------------------ +// var make = domsugar(document); +// +// make( 'p.foo#bar', { hidden: true }, [ make( 'span' ) ] ); +// => +// +// make( '.bar', [ '' ] ); +// make( '.bar', { text: '' } ); +// =>
<b></b>
+// +// make( 'div', [ make( 'b', [ 'Foo', make( 'i' ) ] ) ] ); +// make( 'div', { html: 'Foo' } ); +// =>
Foo
+// +// var myDiv = document.createElement( 'div' ); +// make( myDiv, { id: 'foo' } ); +// =>
+ +(function (window, document) { + 'use strict'; + + // Some properties need to be direct, other are common ones and setting + // them directly is faster than setAttribute. + var direct_property_dict = { + 'class': 'className', + className: 'className', + defaultValue: 'defaultValue', + 'for': 'htmlFor', + html: 'innerHTML', + id: 'id', + name: 'name', + src: 'src', + text: 'textContent', + title: 'title', + value: 'value' + }, + // Object lookup is faster than indexOf. + boolean_property_dict = { + checked: 1, + defaultChecked: 1, + disabled: 1, + hidden: 1, + multiple: 1, + selected: 1 + }; + // splitter = /(#|\.)/; + + function setProperty(el, key, value) { + var prop = direct_property_dict[key]; + if (prop) { + el[prop] = (value === null ? '' : String(value)); + } else if (boolean_property_dict[key]) { + el[key] = !!value; + } else if (value === null) { + el.removeAttribute(key); + } else { + el.setAttribute(key, String(value)); + } + } + + function appendChildren(el, children) { + var i, l, node; + for (i = 0, l = children.length; i < l; i += 1) { + node = children[i]; + if (node) { + if (node instanceof Array) { + appendChildren(el, node); + } else { + if (typeof node === 'string') { + node = document.createTextNode(node); + } + el.appendChild(node); + } + } + } + } + + window.domsugar = function (tag, props, children) { + if (props instanceof Array) { + children = props; + props = null; + } + var el, + prop; +/* + if ( !tag ) { tag = 'div'; createDocumentFragment } + + var parts, name, el, + i, j, l, node, prop; + if ( typeof tag === 'string' && splitter.test( tag ) ) { + parts = tag.split( splitter ); + tag = parts[0]; + if ( !props ) { props = {}; } + for ( i = 1, j = 2, l = parts.length; j < l; i += 2, j += 2 ) { + name = parts[j]; + if ( parts[i] === '#' ) { + props.id = name; + } else { + props.className = props.className ? + props.className + ' ' + name : name; + } + } + } + el = typeof tag === 'string' ? doc.createElement( tag ) : tag; +*/ + if (typeof tag === 'string') { + el = document.createElement(tag); + } else if (tag) { + el = tag; + // Empty the element + while (el.firstChild) { + el.firstChild.remove(); + } + } else { + el = document.createDocumentFragment(); + } + if (props) { + for (prop in props) { + if (props.hasOwnProperty(prop) && (props[prop] !== undefined)) { + setProperty(el, prop, props[prop]); + } + } + } + if (children) { + appendChildren(el, children); + } + return el; + }; + + /*global document, window*/ +}(window, document)); \ No newline at end of file diff --git a/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_domsugar_js.xml b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_domsugar_js.xml new file mode 100644 index 00000000000..7977e3a5fcb --- /dev/null +++ b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_domsugar_js.xml @@ -0,0 +1,330 @@ + + + + + + + + + + _Access_contents_information_Permission + + + Anonymous + Assignee + Assignor + Associate + Auditor + Manager + + + + + _Add_portal_content_Permission + + + Assignee + Assignor + Manager + + + + + _Change_local_roles_Permission + + + Assignor + Manager + + + + + _Modify_portal_content_Permission + + + Assignee + Assignor + Manager + + + + + _View_Permission + + + Anonymous + Assignee + Assignor + Associate + Auditor + Manager + + + + + content_md5 + + + + + + default_reference + domsugar.js + + + description + + + + + + id + rjs_domsugar_js + + + language + en + + + portal_type + Web Script + + + short_title + + + + + + title + domsugar + + + version + 001 + + + workflow_history + + AAAAAAAAAAI= + + + + + + + + + + + + + data + + + + document_publication_workflow + + AAAAAAAAAAM= + + + + edit_workflow + + AAAAAAAAAAQ= + + + + processing_status_workflow + + AAAAAAAAAAU= + + + + + + + + + + + + + + + + _log + + + + + action + publish_alive + + + actor + zope + + + comment + + + + error_message + + + + time + + + + + + + + + + + 1576588231.7 + UTC + + + + + + + validation_state + published_alive + + + + + + + + + + + + + + + + _log + + + + + action + edit + + + actor + zope + + + comment + + + + + + error_message + + + + serial + 980.35510.34638.45021 + + + state + current + + + time + + + + + + + + + + + 1576588238.9 + UTC + + + + + + + + + + + + + + + + + + + + _log + + + + + action + detect_converted_file + + + actor + zope + + + comment + + + + error_message + + + + external_processing_state + converted + + + serial + 0.0.0.0 + + + time + + + + + + + + + + + 1576588149.35 + UTC + + + + + + + + + + + + + -- 2.30.9 From 4294185e3bfe39c240cb9a4bc087edfd727aa79a Mon Sep 17 00:00:00 2001 From: Romain Courteaud Date: Thu, 16 Jan 2020 15:40:15 +0000 Subject: [PATCH 02/42] erp5_document_scanner: fully erase the gadget DOM when changing its state Stop relying of CSS hide/show attributes. Instead, only put needed elements in the DOM. --- .../rjs_gadget_document_scanner_css.css | 14 +- .../rjs_gadget_document_scanner_css.xml | 237 +- .../rjs_gadget_document_scanner_html.html | 6 +- .../rjs_gadget_document_scanner_html.xml | 503 +-- .../rjs_gadget_document_scanner_js.js | 644 +-- .../rjs_gadget_document_scanner_js.xml | 3684 +---------------- ...etPreferredCropperSettingsFromSelection.py | 1 - ...Base_uploadDocumentFromCameraByActivity.py | 7 +- .../your_document_scanner_gadget.xml | 2 +- ..._keep_last_workflow_history_only_path_list | 3 + .../testScanDocument.zpt | 28 +- 11 files changed, 443 insertions(+), 4686 deletions(-) create mode 100644 bt5/erp5_document_scanner/bt/template_keep_last_workflow_history_only_path_list diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css index 7bc0cceb982..b56163ec456 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css @@ -7,7 +7,7 @@ div[data-gadget-scope="field_your_document_scanner_gadget"] { text-align: center; } -.video, .photo, .camera-output { +.video, .photo, .camera-output, .canvas { max-width: 100%; width: auto; max-height: 500px; @@ -17,11 +17,11 @@ div[data-gadget-scope="field_your_document_scanner_gadget"] { .camera-input, .camera-output { min-height: 360px; - display: none; + // display: none; } .canvas { - display: none; + // display: none; filter: brightness(1); } @@ -58,12 +58,20 @@ div[data-gadget-scope="field_your_document_scanner_gadget"] { padding: 3pt; border: 1px solid rgba(0, 0, 0, 0.14); border-radius: 0.325em; + display: "inline-block"; + margin-right: 6pt; +} + +.ui-btn-icon-left:before { + margin-right: 6pt; } +/* .take-picture-btn, .capture-btn, .confirm-btn, .reset-btn, .confirm-btn, .change-camera-btn, .edit-btn { display: none; } +*/ .contentarea { font-size: 16px; diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml index 5b92866dd88..4f87c3300b9 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml @@ -69,7 +69,9 @@ content_type - text/css + + + default_reference @@ -167,9 +169,7 @@ action - - - + publish_alive actor @@ -188,50 +188,11 @@ - + - - - 1574261202.27 - UTC - - - - - - - validation_state - draft - - - - - action - publish_alive - - - actor - zope - - - comment - - - - error_message - - - - time - - - - - - 1574261323.29 @@ -262,51 +223,6 @@ _log - - - action - edit - - - actor - zope - - - comment - - - - error_message - - - - serial - 0.0.0.0 - - - state - current - - - time - - - - - - - - - - - 1574261202.26 - UTC - - - - - - action @@ -328,7 +244,7 @@ serial - 979.60822.46188.23978 + 981.19198.64390.32238 state @@ -338,148 +254,15 @@ time - - - - - - - 1574261231.12 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60823.12148.22101 - - - state - current - - - time - - - - - - - - - 1574261265.43 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 980.2211.58828.61730 - - - state - current - - - time - - - - - - - - - 1574688148.62 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 980.2402.31270.49459 - - - state - current - - - time - - - + + + - 1574697983.15 + 1579541862.76 UTC diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html index 33040111f81..83d6a1d27c4 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html @@ -14,11 +14,13 @@ + Gadget Document Scanner -
+
+ \ No newline at end of file diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml index 53e20769df4..db8e9b7cfd3 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml @@ -167,9 +167,7 @@ action - - - + publish_alive actor @@ -188,50 +186,11 @@ - + - - - 1574261595.83 - UTC - - - - - - - validation_state - draft - - - - - action - publish_alive - - - actor - zope - - - comment - - - - error_message - - - - time - - - - - - 1574261995.43 @@ -262,96 +221,6 @@ _log - - - action - edit - - - actor - zope - - - comment - - - - error_message - - - - serial - 0.0.0.0 - - - state - current - - - time - - - - - - - - - - - 1574261595.83 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60829.17295.36334 - - - state - current - - - time - - - - - - - - - 1574261616.19 - UTC - - - - - - action @@ -373,7 +242,7 @@ serial - 979.60829.39532.12458 + 981.18960.58948.16110 state @@ -383,373 +252,15 @@ time - - - - - - - 1574261638.39 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60835.60550.28808 - - - state - current - - - time - - - - - - - - - 1574262871.91 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60850.34860.27477 - - - state - current - - - time - - - - - - - - - 1574263390.85 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 980.2211.58828.61730 - - - state - current - - - time - - - - - - - - - 1574679628.14 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 980.2260.30741.13158 - - - state - current - - - time - - - - - - - - - 1574679704.62 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 980.2261.48739.22886 - - - state - current - - - time - - - - - - - - - 1574686933.44 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 980.2382.14691.53162 - - - state - current - - - time - - - - - - - - - 1574687074.8 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 980.2384.38015.42888 - - - state - current - - - time - - - - - - - - - 1574688159.56 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 980.2402.43212.46062 - - - state - current - - - time - - - + + + - 1574698038.29 + 1579527559.65 UTC diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 73007963db8..5622babd71c 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -1,93 +1,85 @@ -/*jslint indent: 2 */ -/*global rJS, RSVP, window, navigator, Cropper, Promise, JSON, jIO*/ -(function (rJS, RSVP, window, navigator, Cropper, Promise, JSON, jIO) { +/*jslint indent: 2, unparam: true */ +/*global rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap*/ +(function (rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap) { "use strict"; - function drawCanvas(gadget, img) { - var ratio, x, y, - root = gadget.element, - canvas = root.querySelector("canvas"); - canvas.width = gadget.props.image_width; - canvas.height = gadget.props.image_height; - ratio = Math.min(canvas.width / img.width, canvas.height / img.height); - x = (canvas.width - img.width * ratio) / 2; - y = (canvas.height - img.height * ratio) / 2; - - canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height); - canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height, x, y, img.width * ratio, img.height * ratio); - - //contrastImage(canvas, canvas, 10); - - root.querySelector(".camera-output").style.display = ""; - if (gadget.props.cropper) { - gadget.props.cropper.destroy(); - } - // creating Cropper is asynchronous - return new RSVP.Promise(function (resolve) { - gadget.props.cropper = new Cropper(root.querySelector('.photo'), { - data: gadget.props.preferred_cropped_canvas_data, - ready: resolve - }); + ////////////////////////////////////////////////// + // Browser API to promise + ////////////////////////////////////////////////// + function promiseUserMedia(device_id) { + return navigator.mediaDevices.getUserMedia({ + video: { + deviceId: { + exact: device_id + } + }, + audio: false }); } - function takePicture(gadget) { - var el = gadget.element, - image_capture = gadget.props.image_capture; - return new RSVP.Queue() - .push(function () { - return image_capture.takePhoto({imageWidth: gadget.props.image_width}); - }) - .push(function (blob) { - return jIO.util.readBlobAsDataURL(blob); - }) - .push(function (result) { - var photoInput = el.querySelector(".photoInput"), - photo = el.querySelector("img"), - data_str = result.target.result; - - photo.setAttribute("src", data_str); - photoInput.setAttribute("value", data_str.split(",")[1]); - return drawCanvas(gadget, photo); - }); - } + function handleUserMedia(device_id, callback) { + // Do not modify this function! + // There is no need to add the gadget logic inside + var stream; - function enableButton(root) { - [".reset-btn", ".take-picture-btn", - ".confirm-btn", ".change-camera-btn"].forEach(function (e) { - root.querySelector(e).disabled = false; - }); - } + function canceller() { + if (stream !== undefined) { + // Stop the streams + stream.getTracks().forEach(function (track) { + track.stop(); + }); + } + } - function setPageOne(gadget) { - var root = gadget.element; - root.querySelector(".page-number").innerText = gadget.props.page_number; - root.querySelector(".reset-btn").style.display = "none"; - root.querySelector(".take-picture-btn").style.display = "inline-block"; - root.querySelector(".confirm-btn").style.display = "none"; - root.querySelector(".camera-input").style.display = ""; - if (gadget.props.camera_list.length > 1) { - root.querySelector(".change-camera-btn").style.display = "inline-block"; + function waitForStream(resolve, reject) { + new RSVP.Queue() + .push(function () { + return promiseUserMedia(device_id); + }) + .push(function (result) { + stream = result; + return callback(stream); + }) + .push(undefined, function (error) { + if (!(error instanceof RSVP.CancellationError)) { + canceller(); + reject(error); + } + }); } - return enableButton(root); + return new RSVP.Promise(waitForStream, canceller); } - function setPageTwo(root) { - root.querySelector(".reset-btn").style.display = "inline-block"; - root.querySelector(".confirm-btn").style.display = "inline-block"; - root.querySelector(".take-picture-btn").style.display = "none"; - root.querySelector(".camera-input").style.display = "none"; - root.querySelector(".camera-output").style.display = ""; - root.querySelector(".change-camera-btn").style.display = "none"; - } + function handleCropper(element, data, callback) { + var cropper; - function disableButton(root) { - [".reset-btn", ".take-picture-btn", - ".confirm-btn", ".change-camera-btn"].forEach(function (e) { - root.querySelector(e).disabled = true; - }); + function canceller() { + cropper.destroy(); + } + + // creating Cropper is asynchronous + return new RSVP.Promise(function (resolve, reject) { + cropper = new Cropper(element, { + data: data, + ready: function () { + return new RSVP.Queue() + .push(function () { + return callback(cropper); + }) + .push(undefined, function (error) { + if (!(error instanceof RSVP.CancellationError)) { + canceller(); + reject(error); + } + }); + } + }); + }, canceller); } + ////////////////////////////////////////////////// + // helper function + ////////////////////////////////////////////////// /*function contrastImage(input, output, contrast) { var i, outputContext, @@ -95,7 +87,6 @@ imageData = inputContext.getImageData(0, 0, input.width, input.height), data = imageData.data, factor = (259 * (contrast + 255)) / (255 * (259 - contrast)); - for (i = 0; i < data.length; i += 4) { data[i] = factor * (data[i] - 128) + 128; data[i + 1] = factor * (data[i + 1] - 128) + 128; @@ -114,7 +105,6 @@ imageData = inputContext.getImageData(0, 0, input.width, input.height), data = imageData.data, arraylength = input.width * input.height * 4; - //gray = 0.3*R + 0.59*G + 0.11*B // http://www.tannerhelland.com/3643/grayscale-image-algorithm-vb6/ for (i = arraylength - 1; i > 0; i -= 4) { @@ -125,7 +115,6 @@ } outputContext = outputCanvas.getContext("2d"); outputContext.putImageData(imageData, 0, 0); - data = canvas.toDataURL("image/png"); output.setAttribute("src", data); if (cropper) { @@ -142,203 +131,331 @@ }); }*/ - function handleUserMedia(gadget, callback) { - var stream, - video = gadget.props.video; + function getVideoDeviceList() { + if (!navigator.mediaDevices) { + throw new Error("mediaDevices is not supported"); + } - video.autoplay = "autoplay"; + return new RSVP.Queue() + .push(function () { + return navigator.mediaDevices.enumerateDevices(); + }) + .push(function (info_list) { + var j, + device, + len = info_list.length, + device_list = []; - function canceller() { - if (stream !== undefined) { - // Stop the streams - stream.getTracks().forEach(function (track) { - track.stop(); - }); - } - } + for (j = len - 1; j >= 0; j -= 1) { + // trick to select back camera in mobile + device = info_list[j]; + if (device.kind === 'videoinput') { + device_list.push(device); + } + } + return device_list; + }); + } - function waitForStream() { - return new RSVP.Queue() - .push(function () { - return navigator.mediaDevices.getUserMedia({ - video: { - deviceId: { - exact: gadget.props.device_id - } + function selectMediaDevice(current_device_id, force_new_device) { + return getVideoDeviceList() + .push(function (info_list) { + var j, + device, + len = info_list.length; + + for (j = len - 1; j >= 0; j -= 1) { + // trick to select back camera in mobile + device = info_list[j]; + if (device.kind === 'videoinput') { + if ((!current_device_id) || + (force_new_device && (device.deviceId !== current_device_id)) || + (!force_new_device && (device.deviceId === current_device_id))) { + return device.deviceId; } - }); - }) - .push(function (mediaStream) { - stream = mediaStream; - video.srcObject = mediaStream; - return callback(gadget, stream); - }) - .push(undefined, function (error) { - if (!(error instanceof RSVP.CancellationError)) { - canceller(); } - }); + } + throw new Error("no media found"); + }); + } + + ////////////////////////////////////////////////// + // Private gadget function + ////////////////////////////////////////////////// + function addDetachedPromise(gadget, key, promise) { + // XXX TODO Handle error + if (gadget.detached_promise_dict.hasOwnProperty(key)) { + gadget.detached_promise_dict[key].cancel('Replacing key: ' + key); } + gadget.detached_promise_dict[key] = new RSVP.Queue() + .push(function () { + return promise; + }) + .push(undefined, function (error) { + // Crash the gadget if the detached promise raise an unexpected error + gadget.raise(error); + }); + } - return new RSVP.Promise(waitForStream, canceller); + // Display the video stream from a media source + function renderVideoCapture(gadget) { + var video; + return RSVP.Queue() + .push(function () { + var defer = RSVP.defer(); + addDetachedPromise(gadget, 'media_stream', + handleUserMedia(gadget.state.device_id, defer.resolve)); + return defer.promise; + }) + .push(function (media_stream) { + video = document.createElement('video'); + video.srcObject = media_stream; + video.autoplay = "autoplay"; + video.loop = "loop"; + video.muted = "muted"; + + return RSVP.any([ + // Wait for the video to be ready + promiseEventListener(video, 'loadedmetadata', true), + promiseEventListener(video, 'canplaythrough', true), + promiseEventListener(video, 'error', true, function () { + throw new Error("Can't play the video file"); + }) + ]); + }) + + .push(function () { + video.play(); + return RSVP.all([ + getVideoDeviceList(), + gadget.getTranslationList(["Take Picture", "Change Camera"]) + ]); + }) + .push(function (result_list) { + var button_list = [ + domsugar('button', {type: 'button', + class: 'take-picture-btn ui-btn-icon-left ui-icon-circle', + text: result_list[1][0] + }) + ], + div; + // Only display the change camera if device has at least 2 cameras + if (result_list[0].length > 1) { + button_list.push( + domsugar('button', {type: 'button', + class: 'change-camera-btn ui-icon-refresh ui-btn-icon-left', + text: result_list[1][1] + }) + ); + } + + div = domsugar('div', {class: 'camera'}, [ + domsugar('div', {class: 'camera-header'}, [ + domsugar('h4', [ + 'Page ', + domsugar('label', {class: 'page-number', text: gadget.state.page}) + ]) + ]), + domsugar('div', {class: 'camera-input'}, [video]), + domsugar('div', {class: 'edit-picture'}, button_list) + ]); + + gadget.element.replaceChild(div, gadget.element.firstElementChild); + + }); } - function gotStream(gadget, mediaStream) { + // Capture the media stream + function captureAndRenderPicture(gadget) { + var image_capture = new window.ImageCapture( + gadget.element.querySelector('video').srcObject.getVideoTracks()[0] + ), + div; return new RSVP.Queue() .push(function () { - var image_capture; - image_capture = new window.ImageCapture(mediaStream.getVideoTracks()[0]); - gadget.props.image_capture = image_capture; return image_capture.getPhotoCapabilities(); }) - .push(function (photoCapabilities) { - gadget.props.image_width = photoCapabilities.imageWidth.max; - gadget.props.image_height = photoCapabilities.imageHeight.max; - return gadget.props.video.play(); + .push(function (capabilities) { + return image_capture.takePhoto({imageWidth: capabilities.imageWidth.max}); }) - .push(function () { - return setPageOne(gadget); + .push(function (blob) { + gadget.detached_promise_dict.media_stream.cancel('Not needed anymore, as captured'); + return RSVP.all([ + gadget.getTranslationList(["Reset", "Confirm"]), + createImageBitmap(blob) + ]); + }) + .push(function (result_list) { + var // blob_url = URL.createObjectURL(blob), + // img = domsugar('img', {src: blob_url}); + bitmap = result_list[1], + canvas = domsugar('canvas', {class: 'canvas'}), + defer = RSVP.defer(); + + // Prepare the cropper canvas + canvas.width = bitmap.width; + canvas.height = bitmap.height; + canvas.getContext('2d').drawImage(bitmap, 0, 0); + + div = domsugar('div', {class: 'camera'}, [ + domsugar('div', {class: 'camera-header'}, [ + domsugar('h4', [ + 'Page ', + domsugar('label', {class: 'page-number', text: gadget.state.page}) + ]) + ]), + canvas, + domsugar('div', {class: 'edit-picture'}, [ + domsugar('button', {type: 'button', + class: 'reset-btn ui-btn-icon-left ui-icon-times', + text: result_list[0][0] + }), + domsugar('button', {type: 'button', + class: 'confirm-btn ui-btn-icon-left ui-icon-check', + text: result_list[0][1] + }) + ]) + ]); + + // XXX How to change the dom only when cropper is ready? + // For now, it needs to access dom element size + gadget.element.replaceChild(div, gadget.element.firstElementChild); + + addDetachedPromise(gadget, 'cropper', + handleCropper(canvas, + gadget.state.preferred_cropped_canvas_data, + defer.resolve)); + return defer.promise; + }) + .push(function (cropper) { + gadget.cropper = cropper; }); } - function startStream(gadget) { - return handleUserMedia(gadget, gotStream); + function renderSubmittedPicture(gadget) { + var div = domsugar('div', {class: 'camera'}, [ + domsugar('div', {class: 'camera-header'}, [ + domsugar('h4', [ + 'Page ', + domsugar('label', {class: 'page-number', text: gadget.state.page}) + ]) + ]), + domsugar('img', {src: gadget.state.blob_url}) + ]); + + // XXX How to change the dom only when cropper is ready? + // For now, it needs to access dom element size + gadget.element.replaceChild(div, gadget.element.firstElementChild); } + ////////////////////////////////////////////////// + // Gadget API + ////////////////////////////////////////////////// rJS(window) - .declareAcquiredMethod( - "submitDialogWithCustomDialogMethod", - "submitDialogWithCustomDialogMethod" - ) - .declareAcquiredMethod("getTranslationList", "getTranslationList") - .declareAcquiredMethod("notifySubmitted", "notifySubmitted") - .declareJob("startStream", function () { - return startStream(this); - }) .ready(function () { - this.props = { - video: this.element.querySelector(".video") - }; + this.detached_promise_dict = {}; }) - .declareMethod('render', function (options) { - var root = this.element, - camera_list = [], - gadget = this; - - return this.getTranslationList(["Webcam is not available", "Reset", "Take Picture", "Confirm", "Edit", "Change Camera"]) - .push(function (result_list) { - var i, - button_list = root.querySelectorAll("button"); - for (i = 0; i < button_list.length; i += 1) { - button_list[i].innerText = " " + result_list[i + 1]; - } - root.querySelector("video").innerText = result_list[0]; - }) - .push(function () { - var preferred_cropped_canvas_data = gadget.props.preferred_cropped_canvas_data; - preferred_cropped_canvas_data = preferred_cropped_canvas_data || JSON.parse(options.preferred_cropped_canvas_data); - gadget.props.dialog_method = preferred_cropped_canvas_data.dialog_method; - // Clear photo input - root.querySelector('.photoInput').value = ""; - gadget.props.page_number = parseInt(root.querySelector('input[name="page-number"]').value, 10); - root.querySelector(".camera-input").style.display = ""; - root.querySelector(".camera-output").style.display = "none"; - - if (!navigator.mediaDevices) { - throw ("mediaDevices is not supported"); - } - gadget.props.preferred_cropped_canvas_data = preferred_cropped_canvas_data; - return navigator.mediaDevices.enumerateDevices(); - }) - .push(function (info_list) { - var j, - device, - len = info_list.length; - - if (camera_list.length === 0) { - for (j = 0; j < len; j += 1) { - device = info_list[j]; - if (device.kind === 'videoinput') { - camera_list.push(device); - } - } - } - if (camera_list.length >= 1) { - // trick to select back camera in mobile - gadget.props.device_id = camera_list[camera_list.length - 1].deviceId; + .declareJob('raise', function (error) { + throw error; + }) + .declareService(function handleDetachedPromiseDict() { + // This service is responsable to cancel all ongoing detached promises + // if the gadget is removed from the page + var gadget = this; + return new RSVP.Promise(function () {return; }, function canceller(msg) { + var key; + for (key in gadget.detached_promise_dict) { + if (gadget.detached_promise_dict.hasOwnProperty(key)) { + gadget.detached_promise_dict[key].cancel(msg); } - gadget.props.camera_list = camera_list; - return gadget.startStream(); + } + }); + }) + + .setState({ + display_step: 'display_video', + page: 1 + }) + .declareMethod('render', function (options) { + // This method is called during the ERP5 form rendering + // changeState is used to ensure not resetting the gadget current display + // if not needed + var gadget = this; + return selectMediaDevice(gadget.state.device_id, false) + .push(function (device_id) { + return gadget.changeState({ + dialog_method: options.dialog_method, + preferred_cropped_canvas_data: JSON.parse(options.preferred_cropped_canvas_data), + device_id: device_id, + key: options.key + }); }); }) + + .onStateChange(function () { + var gadget = this; + // ALL DOM modifications must be done only in this method + // this prevent concurrency issue on DOM access + if (gadget.state.display_step === 'display_video') { + return renderVideoCapture(gadget); + } + + if (gadget.state.display_step === 'crop_picture') { + return captureAndRenderPicture(gadget); + } + + if (gadget.state.display_step === 'submitting') { + return renderSubmittedPicture(gadget); + } + + // Ease developper work by raising for not handled cases + throw new Error('Unhandled display step: ' + gadget.state.display_step); + + }) + .declareMethod('getContent', function () { - var input = this.element.querySelector('.photoInput'), + var gadget = this, result = {}; - - result.field_your_document_scanner_gadget = JSON.stringify({ - "input_value": input.value, - "preferred_cropped_canvas_data": this.props.preferred_cropped_canvas_data - }); + if (gadget.state.display_step === 'submitting') { + // do not send any content when sending the final form + result[gadget.state.key] = JSON.stringify({ + input_value: gadget.state.blob_url.split(';')[1].split(',')[1], + preferred_cropped_canvas_data: gadget.state.preferred_cropped_canvas_data + }); + } return result; }) - .onEvent("click", function (evt) { - var e, - new_preferred_cropped_canvas_data, - gadget = this, - camera_list = this.props.camera_list, - root = this.element; - - /*if (evt.target.name === "grayscale") { - return grayscale(root.querySelector(".canvas"), - root.querySelector('.photo')); - }*/ - if (evt.target.className.indexOf("change-camera-btn") !== -1) { - evt.preventDefault(); - for (e in camera_list) { - if (camera_list.hasOwnProperty(e)) { - if (camera_list[e].deviceId !== gadget.props.device_id) { - gadget.props.device_id = camera_list[e].deviceId; - break; - } - } - } - return gadget.startStream(); + .onEvent("click", function (evt) { + // Only handle click on BUTTON element + if (evt.target.tagName !== 'BUTTON') { + return; } + + var gadget = this; + + // Disable any button. It must be managed by this gadget + evt.preventDefault(); + gadget.element.querySelectorAll('button').forEach(function (elt) { + elt.disabled = true; + }); + if (evt.target.className.indexOf("take-picture-btn") !== -1) { - evt.preventDefault(); - return new RSVP.Queue() - .push(function () { - disableButton(root); - root.querySelector(".camera").style.maxWidth = gadget.props.video.offsetWidth + "px"; - return takePicture(gadget); - }) - .push(function () { - root.querySelector(".camera-input").style.display = "none"; - setPageTwo(root); - return enableButton(root); - }); + return gadget.changeState({ + display_step: 'crop_picture' + }); } + if (evt.target.className.indexOf("reset-btn") !== -1) { - evt.preventDefault(); - root.querySelector(".camera-input").style.display = ""; - root.querySelector(".camera-output").style.display = "none"; - root.querySelector('.photoInput').value = ""; - gadget.props.cropper.destroy(); - return setPageOne(gadget); + return gadget.changeState({ + display_step: 'display_video' + }); } + if (evt.target.className.indexOf("confirm-btn") !== -1) { - evt.preventDefault(); - new_preferred_cropped_canvas_data = gadget.props.cropper.getData(); - for (e in new_preferred_cropped_canvas_data) { - if (new_preferred_cropped_canvas_data.hasOwnProperty(e)) { - gadget.props.preferred_cropped_canvas_data[e] = new_preferred_cropped_canvas_data[e]; - } - } return new RSVP.Queue() .push(function () { - var canvas = gadget.props.cropper.getCroppedCanvas(); - disableButton(gadget.element); + var canvas = gadget.cropper.getCroppedCanvas(); return new Promise(function (resolve) { canvas.toBlob(resolve, 'image/jpeg', 0.85); }); @@ -346,22 +463,45 @@ .push(function (blob) { return jIO.util.readBlobAsDataURL(blob); }) - .push(function (result) { - var base64data = result.target.result, - block = base64data.split(";"), - realData = block[1].split(",")[1]; - root.querySelector(".photo").src = base64data; - root.querySelector(".photoInput").value = realData; - gadget.props.cropper.destroy(); + .push(function (evt) { + return gadget.changeState({ + blob_url: evt.target.result, + preferred_cropped_canvas_data: gadget.cropper.getData(), + display_step: 'submitting' + }); }) .push(function () { - return gadget.submitDialogWithCustomDialogMethod(gadget.props.dialog_method); + gadget.detached_promise_dict.cropper.cancel('Not needed anymore, as cropped'); + return gadget.submitDialogWithCustomDialogMethod(gadget.state.dialog_method); }) - .push(function () { - gadget.props.page_number = gadget.props.page_number + 1; - root.querySelector('input[name="page-number"]').value = gadget.props.page_number; + .push(function (evt) { + return gadget.changeState({ + blob_url: undefined, + display_step: 'display_video', + page: gadget.state.page + 1 + }); + }); + } + + if (evt.target.className.indexOf("change-camera-btn") !== -1) { + return selectMediaDevice(gadget.state.device_id, true) + .push(function (device_id) { + return gadget.changeState({ + display_step: 'display_video', + device_id: device_id + }); }); } - }, false, false); -}(rJS, RSVP, window, navigator, Cropper, Promise, JSON, jIO)); \ No newline at end of file + throw new Error('Unhandled button: ' + evt.target.textContent); + }, false, false) + + + .declareAcquiredMethod( + "submitDialogWithCustomDialogMethod", + "submitDialogWithCustomDialogMethod" + ) + .declareAcquiredMethod("getTranslationList", "getTranslationList") + .declareAcquiredMethod("notifySubmitted", "notifySubmitted"); + +}(rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap)); \ No newline at end of file diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 2bdd94c24aa..01aac105a0e 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -169,9 +169,7 @@ action - - - + publish_alive actor @@ -190,50 +188,11 @@ - + - - - 1574261340.43 - UTC - - - - - - - validation_state - draft - - - - - action - publish_alive - - - actor - zope - - - comment - - - - error_message - - - - time - - - - - - 1574261409.0 @@ -285,7 +244,7 @@ serial - 981.11546.40848.57344 + 981.21579.36694.13363 state @@ -296,194 +255,14 @@ - + - 1579082847.27 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 981.11551.29846.29269 - - - state - current - - - time - - - - - - - - - 1579082942.18 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 981.11553.2579.61422 - - - state - current - - - time - - - - - - - - - 1579082950.88 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 981.11553.11938.60603 - - - state - current - - - time - - - - - - - - - 1579082992.83 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 981.11553.57761.12817 - - - state - current - - - time - - - - - - - - - 1579083055.7 + 1579684586.42 UTC @@ -494,22 +273,6 @@ - - _next - - AAAAAAAAAAY= - - - - _prev - - AAAAAAAAAAc= - - - - _tail_count - 75 - @@ -576,3441 +339,4 @@ - - - - - - - - _log - - - - - action - edit - - - actor - zope - - - comment - - - - error_message - - - - serial - 0.0.0.0 - - - state - current - - - time - - - - - - - - - - - 1574261340.43 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60825.480.48810 - - - state - current - - - time - - - - - - - - - 1574261356.64 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60825.46120.29849 - - - state - current - - - time - - - - - - - - - 1574261398.96 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60826.9837.15291 - - - state - current - - - time - - - - - - - - - 1574261577.9 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60828.63254.50278 - - - state - current - - - time - - - - - - - - - 1574262365.84 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60842.6386.64017 - - - state - current - - - time - - - - - - - - - 1574262388.11 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60842.30711.49083 - - - state - current - - - time - - - - - - - - - 1574262460.21 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60843.43928.56746 - - - state - current - - - time - - - - - - - - - 1574262462.44 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60843.43928.56746 - - - state - current - - - time - - - - - - - - - 1574263610.67 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60862.55352.10837 - - - state - current - - - time - - - - - - - - - 1574263793.54 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60865.58483.62873 - - - state - current - - - time - - - - - - - - - 1574263803.34 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60866.3654.22835 - - - state - current - - - time - - - - - - - - - 1574263914.73 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60867.59788.15547 - - - state - current - - - time - - - - - - - - - 1574263994.48 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60869.15817.48384 - - - state - current - - - time - - - - - - - - - 1574264044.06 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60870.4440.37973 - - - state - current - - - time - - - - - - - - - 1574264065.96 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60870.28365.56046 - - - state - current - - - time - - - - - - - - - 1574264073.08 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60870.36143.59784 - - - state - current - - - time - - - - - - - - - 1574264140.93 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.60870.36143.59784 - - - state - current - - - time - - - - - - - - - 1574328581.59 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61945.45445.19456 - - - state - current - - - time - - - - - - - - - 1574328759.88 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61948.43567.53623 - - - state - current - - - time - - - - - - - - - 1574328864.83 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61950.27123.63556 - - - state - current - - - time - - - - - - - - - 1574328881.2 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61950.45006.25873 - - - state - current - - - time - - - - - - - - - 1574328921.25 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61950.45006.25873 - - - state - current - - - time - - - - - - - - - 1574329058.14 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61953.41665.18483 - - - state - current - - - time - - - - - - - - - 1574329244.42 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61956.48527.26880 - - - state - current - - - time - - - - - - - - - 1574329481.16 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61960.44965.38075 - - - state - current - - - time - - - - - - - - - 1574329491.66 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61960.56435.41181 - - - state - current - - - time - - - - - - - - - 1574329517.12 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61961.18710.238 - - - state - current - - - time - - - - - - - - - 1574329524.83 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61961.27126.52992 - - - state - current - - - time - - - - - - - - - 1574329548.23 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61961.52681.35140 - - - state - current - - - time - - - - - - - - - 1574329610.14 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61962.54770.6109 - - - state - current - - - time - - - - - - - - - 1574329618.9 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61962.64337.54272 - - - state - current - - - time - - - - - - - - - 1574329630.75 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61963.11749.30293 - - - state - current - - - time - - - - - - - - - 1574329651.09 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61963.33962.34389 - - - state - current - - - time - - - - - - - - - 1574329665.75 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61963.49975.1860 - - - state - current - - - time - - - - - - - - - 1574329684.61 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61964.5044.3515 - - - state - current - - - time - - - - - - - - - 1574329725.87 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61964.50110.5427 - - - state - current - - - time - - - - - - - - - 1574329792.87 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.61965.57749.53930 - - - state - current - - - time - - - - - - - - - 1574333504.33 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62027.48424.22186 - - - state - current - - - time - - - - - - - - - 1574333540.39 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62028.22281.31658 - - - state - current - - - time - - - - - - - - - 1574333546.63 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62028.29088.60535 - - - state - current - - - time - - - - - - - - - 1574333578.09 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62028.63457.3959 - - - state - current - - - time - - - - - - - - - 1574333588.59 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62029.9391.19456 - - - state - current - - - time - - - - - - - - - 1574333680.86 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62030.44640.11980 - - - state - current - - - time - - - - - - - - - 1574333691.05 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62030.55770.24268 - - - state - current - - - time - - - - - - - - - 1574333695.51 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62030.55770.24268 - - - state - current - - - time - - - - - - - - - 1574333703.68 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62030.55770.24268 - - - state - current - - - time - - - - - - - - - 1574333724.09 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62031.26316.48913 - - - state - current - - - time - - - - - - - - - 1574333754.93 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62031.60011.63180 - - - state - current - - - time - - - - - - - - - 1574333791.01 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62032.33877.17049 - - - state - current - - - time - - - - - - - - - 1574333800.61 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62032.44371.3805 - - - state - current - - - time - - - - - - - - - 1574333880.66 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62034.728.43673 - - - state - current - - - time - - - - - - - - - 1574333903.9 - UTC - - - - - - - - - - - _next - - AAAAAAAAAAc= - - - - _prev - - AAAAAAAAAAQ= - - - - - - - - - - - - - _log - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62034.26107.12646 - - - state - current - - - time - - - - - - - - - - - 1574334426.15 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62043.6727.63897 - - - state - current - - - time - - - - - - - - - 1574334434.07 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62043.15372.1979 - - - state - current - - - time - - - - - - - - - 1574334459.67 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62043.43339.58368 - - - state - current - - - time - - - - - - - - - 1574334473.18 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62043.58096.27869 - - - state - current - - - time - - - - - - - - - 1574334938.08 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62051.41603.16452 - - - state - current - - - time - - - - - - - - - 1574334949.25 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62051.53805.39168 - - - state - current - - - time - - - - - - - - - 1574335000.76 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62052.44527.12595 - - - state - current - - - time - - - - - - - - - 1574335009.11 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62052.53648.38587 - - - state - current - - - time - - - - - - - - - 1574335034.19 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62053.15499.63539 - - - state - current - - - time - - - - - - - - - 1574335260.18 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62057.201.30139 - - - state - current - - - time - - - - - - - - - 1574335275.2 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62057.16612.7850 - - - state - current - - - time - - - - - - - - - 1574335306.78 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62057.51100.60962 - - - state - current - - - time - - - - - - - - - 1574335319.75 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62057.65272.3805 - - - state - current - - - time - - - - - - - - - 1574335338.51 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62058.20221.31044 - - - state - current - - - time - - - - - - - - - 1574335518.14 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62061.19823.20002 - - - state - current - - - time - - - - - - - - - 1574339757.58 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62131.62904.38792 - - - state - current - - - time - - - - - - - - - 1574339781.65 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62132.23662.32563 - - - state - current - - - time - - - - - - - - - 1574339815.15 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62132.60242.36198 - - - state - current - - - time - - - - - - - - - 1574339853.78 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62133.36904.64409 - - - state - current - - - time - - - - - - - - - 1574341175.19 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62155.38441.25361 - - - state - current - - - time - - - - - - - - - 1574341251.38 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 979.62156.56125.51746 - - - state - current - - - time - - - - - - - - - 1574341335.11 - UTC - - - - - - - - - action - edit - - - actor - zope - - - comment - - - - - - error_message - - - - serial - 981.4762.16692.46353 - - - state - current - - - time - - - - - - - - - 1579082557.27 - UTC - - - - - - - - - - - _next - - AAAAAAAAAAQ= - - - - _prev - - AAAAAAAAAAY= - - - - _tail_count - 52 - - - - diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredCropperSettingsFromSelection.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredCropperSettingsFromSelection.py index 9fd6782b15f..9be6f0ebb26 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredCropperSettingsFromSelection.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredCropperSettingsFromSelection.py @@ -7,6 +7,5 @@ selection_mapping = portal.portal_selections.getSelectionParamsFor( REQUEST=context.REQUEST) or {} canvas_data = selection_mapping.get(context.REQUEST["HTTP_USER_AGENT"]) or {} -canvas_data["dialog_method"] = context.Base_storeDocumentFromCameraInActiveProcess.getId() return json.dumps(canvas_data) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.py index 88daa42965f..b01c490bc49 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.py @@ -1,16 +1,11 @@ portal = context.getPortalObject() translateString = portal.Base_translateString -active_process = context.Base_storeDocumentFromCameraInActiveProcess( - active_process_url=active_process_url, - batch_mode=True, - **kw) - # Avoid to pass huge images to the activity kw.pop("your_document_scanner_gadget", None) context.activate().Base_uploadDocumentFromCamera( - active_process_url=active_process.getRelativeUrl(), + active_process_url=active_process_url, **kw) return context.Base_redirect('view', diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml index 3df51736aa2..6b2b2c6e927 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml @@ -141,7 +141,7 @@ _text - python: [(\'preferred_cropped_canvas_data\', context.Base_getPreferredCropperSettingsFromSelection()),] + python: [(\'dialog_method\', \'Base_storeDocumentFromCameraInActiveProcess\'), (\'preferred_cropped_canvas_data\', context.Base_getPreferredCropperSettingsFromSelection()),] diff --git a/bt5/erp5_document_scanner/bt/template_keep_last_workflow_history_only_path_list b/bt5/erp5_document_scanner/bt/template_keep_last_workflow_history_only_path_list new file mode 100644 index 00000000000..87e5720ada3 --- /dev/null +++ b/bt5/erp5_document_scanner/bt/template_keep_last_workflow_history_only_path_list @@ -0,0 +1,3 @@ +web_page_module/rjs_gadget_document_scanner_css +web_page_module/rjs_gadget_document_scanner_html +web_page_module/rjs_gadget_document_scanner_js \ No newline at end of file diff --git a/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt b/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt index 4292695a2d7..bf7ad3d3da0 100644 --- a/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt +++ b/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt @@ -101,20 +101,15 @@ //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] - - waitForCondition - selenium.browserbot.getCurrentWindow().document.querySelector("video").readyState == 4 - 30000 - click //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] - waitForCondition - selenium.browserbot.getCurrentWindow().document.querySelector(".confirm-btn").style.display != "none" - 30000 + waitForElementPresent + //button[@class="reset-btn ui-btn-icon-left ui-icon-times"] + click @@ -122,9 +117,9 @@ - waitForCondition - selenium.browserbot.getCurrentWindow().document.querySelector(".confirm-btn").style.display == "none" - 30000 + waitForElementPresent + //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] + click @@ -132,9 +127,9 @@ - waitForCondition - selenium.browserbot.getCurrentWindow().document.querySelector(".confirm-btn").style.display != "none" - 3000 + waitForElementPresent + //button[@class="confirm-btn ui-btn-icon-left ui-icon-check"] + click @@ -149,11 +144,6 @@ - storeValue //input[@id="field_your_active_process_url"] -- 2.30.9 From 89c4ef1c39e2350fa2d9f83afceab95d5586d88e Mon Sep 17 00:00:00 2001 From: Romain Courteaud Date: Wed, 22 Jan 2020 10:52:23 +0000 Subject: [PATCH 03/42] WIP erp5_document_scanner: upload images asynchronously to improve the usability XXX Allow to show previously uploaded image. Allow to delete or retry uploading. Form submittion only send the active process informations. --- .../rjs_gadget_document_scanner_js.js | 279 ++++++++++++++---- .../rjs_gadget_document_scanner_js.xml | 4 +- 2 files changed, 223 insertions(+), 60 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 5622babd71c..f350fcba537 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -77,6 +77,40 @@ }, canceller); } + function handleAsyncStore(gadget, blob_page) { + return new RSVP.Queue() + .push(function () { + // XXX TODO: jio.util.ajax with + /* + JSON.stringify({ + input_value: gadget.state.blob_url_XXX.split(';')[1].split(',')[1], + // preferred_cropped_canvas_data: gadget.state.preferred_cropped_canvas_data + }) + */ + function getRandomInt(max) { + return Math.floor(Math.random() * Math.floor(max)); + } + // XXX long or not, working or not, who knows? + return RSVP.any([ + RSVP.delay(2000 + getRandomInt(3000)), + RSVP.timeout(2000 + getRandomInt(3000)) + ]); + }) + .push(function () { + var state_dict = {}; + state_dict['blob_state_' + blob_page] = 'stored'; + // XXX TODO: ajax must return a active process image content UUID + // which should be sent in the final form submittion + state_dict['blob_uuid_' + blob_page] = 'XXX'; + return gadget.changeState(state_dict); + }, function () { + // XXX TODO: Handle error case + var state_dict = {}; + state_dict['blob_state_' + blob_page] = 'failed'; + return gadget.changeState(state_dict); + }); + } + ////////////////////////////////////////////////// // helper function ////////////////////////////////////////////////// @@ -197,6 +231,39 @@ }); } + function buildPreviousThumbnailDom(gadget) { + var i, + len = gadget.state.page_count, + thumbnail_dom_list = []; + for (i = 0; i < len; i += 1) { + // XXX TODO: show nice looking thumbnail + // from gadget.state.blob_url_i + // XXX TODO translation + right term + // XXX TODO display a loader when sending + if (gadget.state['blob_state_' + i] !== 'deleted') { + thumbnail_dom_list.push(domsugar('button', {type: 'button', + text: 'Image' + (i + 1) + ' (' + gadget.state['blob_state_' + i] + ')', + // Do not allow to show again the current image + // or do not allow to show sending image (to simplify button management) + disabled: (i === gadget.state.page) || (gadget.state['blob_state_' + i] === 'sending'), + class: 'show-img', + 'data-page': i + })); + } + } + + // Always add a button to generate a new image + // XXX TODO translation + right term + thumbnail_dom_list.push(domsugar('button', {type: 'button', + text: 'New', + // Do not allow to show again the current image + disabled: (len === gadget.state.page - 1), + class: 'new-btn' + })); + + return domsugar('ol', thumbnail_dom_list); + } + // Display the video stream from a media source function renderVideoCapture(gadget) { var video; @@ -257,7 +324,8 @@ ]) ]), domsugar('div', {class: 'camera-input'}, [video]), - domsugar('div', {class: 'edit-picture'}, button_list) + domsugar('div', {class: 'edit-picture'}, button_list), + buildPreviousThumbnailDom(gadget) ]); gadget.element.replaceChild(div, gadget.element.firstElementChild); @@ -314,7 +382,8 @@ class: 'confirm-btn ui-btn-icon-left ui-icon-check', text: result_list[0][1] }) - ]) + ]), + buildPreviousThumbnailDom(gadget) ]); // XXX How to change the dom only when cropper is ready? @@ -333,19 +402,44 @@ } function renderSubmittedPicture(gadget) { - var div = domsugar('div', {class: 'camera'}, [ - domsugar('div', {class: 'camera-header'}, [ - domsugar('h4', [ - 'Page ', - domsugar('label', {class: 'page-number', text: gadget.state.page}) - ]) - ]), - domsugar('img', {src: gadget.state.blob_url}) - ]); - - // XXX How to change the dom only when cropper is ready? - // For now, it needs to access dom element size - gadget.element.replaceChild(div, gadget.element.firstElementChild); + return gadget.getTranslationList(["Delete", "Retry"]) + .push(function (translation_list) { + var button_list = [ + // XXX TODO: improve icon + domsugar('button', {type: 'button', + class: 'delete-btn ui-btn-icon-left ui-icon-times', + text: translation_list[0] + }) + ], + div; + + if (gadget.state['blob_state_' + gadget.state.page] === 'failed') { + button_list.push( + // XXX TODO improve icon + domsugar('button', {type: 'button', + class: 'retry-btn ui-btn-icon-left ui-icon-times', + text: translation_list[1] + }) + ); + } + + div = domsugar('div', {class: 'camera'}, [ + domsugar('div', {class: 'camera-header'}, [ + domsugar('h4', [ + 'Page ', + domsugar('label', {class: 'page-number', text: gadget.state.page + 1}) + ]) + ]), + domsugar('img', {src: gadget.state['blob_url_' + gadget.state.page]}), + // XXX TODO: why is the button rendering different from the other pages? + domsugar('div', {class: 'edit-picture'}, button_list), + buildPreviousThumbnailDom(gadget) + ]); + + // XXX How to change the dom only when cropper is ready? + // For now, it needs to access dom element size + gadget.element.replaceChild(div, gadget.element.firstElementChild); + }); } ////////////////////////////////////////////////// @@ -374,7 +468,8 @@ .setState({ display_step: 'display_video', - page: 1 + page: 1, + page_count: 0 }) .declareMethod('render', function (options) { // This method is called during the ERP5 form rendering @@ -387,43 +482,50 @@ dialog_method: options.dialog_method, preferred_cropped_canvas_data: JSON.parse(options.preferred_cropped_canvas_data), device_id: device_id, - key: options.key + key: options.key, + first_render: true }); }); }) - .onStateChange(function () { - var gadget = this; + .onStateChange(function (modification_dict) { + var gadget = this, + display_step, + thumbnail_container; // ALL DOM modifications must be done only in this method // this prevent concurrency issue on DOM access - if (gadget.state.display_step === 'display_video') { + + // Only refresh the full gadget content after the first render call + // or if the display_step is modified + // or if displaying another image + if (modification_dict.first_render || modification_dict.hasOwnProperty('page')) { + display_step = gadget.state.display_step; + } else { + display_step = modification_dict.display_step; + } + if (display_step === 'display_video') { return renderVideoCapture(gadget); } - - if (gadget.state.display_step === 'crop_picture') { + if (display_step === 'crop_picture') { return captureAndRenderPicture(gadget); } - - if (gadget.state.display_step === 'submitting') { + if (display_step === 'show_picture') { return renderSubmittedPicture(gadget); } + if (display_step) { + // Ease developper work by raising for not handled cases + throw new Error('Unhandled display step: ' + gadget.state.display_step); + } - // Ease developper work by raising for not handled cases - throw new Error('Unhandled display step: ' + gadget.state.display_step); - - }) + // Only refresh the thumbnail list + // if display_step is not modified + // XXX TODO use a more precise selector + thumbnail_container = gadget.element.querySelector('ol'); + thumbnail_container.parentElement.replaceChild( + buildPreviousThumbnailDom(gadget), + thumbnail_container + ); - .declareMethod('getContent', function () { - var gadget = this, - result = {}; - if (gadget.state.display_step === 'submitting') { - // do not send any content when sending the final form - result[gadget.state.key] = JSON.stringify({ - input_value: gadget.state.blob_url.split(';')[1].split(',')[1], - preferred_cropped_canvas_data: gadget.state.preferred_cropped_canvas_data - }); - } - return result; }) .onEvent("click", function (evt) { @@ -432,7 +534,8 @@ return; } - var gadget = this; + var gadget = this, + state_dict; // Disable any button. It must be managed by this gadget evt.preventDefault(); @@ -452,6 +555,22 @@ }); } + if (evt.target.className.indexOf("new-btn") !== -1) { + return gadget.changeState({ + display_step: 'display_video', + page: gadget.state.page_count + 1 + }); + } + + if (evt.target.className.indexOf("delete-btn") !== -1) { + state_dict = { + display_step: 'display_video', + page: gadget.state.page_count + 1 + }; + state_dict['blob_state_' + gadget.state.page] = 'deleted'; + return gadget.changeState(state_dict); + } + if (evt.target.className.indexOf("confirm-btn") !== -1) { return new RSVP.Queue() .push(function () { @@ -464,25 +583,40 @@ return jIO.util.readBlobAsDataURL(blob); }) .push(function (evt) { - return gadget.changeState({ - blob_url: evt.target.result, + state_dict = { preferred_cropped_canvas_data: gadget.cropper.getData(), - display_step: 'submitting' - }); + display_step: 'display_video', + page: gadget.state.page + 1, + page_count: gadget.state.page_count + 1 + }; + // Keep image date, as user may need to display it again + state_dict['blob_url_' + gadget.state.page_count] = evt.target.result; + state_dict['blob_state_' + gadget.state.page_count] = 'sending'; + return gadget.changeState(state_dict); }) .push(function () { + // XXX TODO Send the image to ERP5 + // XXX Ensure that you have the active process relative url + addDetachedPromise(gadget, 'ajax_' + (gadget.state.page_count - 1), + handleAsyncStore(gadget, gadget.state.page_count - 1)); + gadget.detached_promise_dict.cropper.cancel('Not needed anymore, as cropped'); - return gadget.submitDialogWithCustomDialogMethod(gadget.state.dialog_method); - }) - .push(function (evt) { - return gadget.changeState({ - blob_url: undefined, - display_step: 'display_video', - page: gadget.state.page + 1 - }); }); } + if (evt.target.className.indexOf("retry-btn") !== -1) { + // XXX TODO Send the image to ERP5 + // XXX Ensure that you have the active process relative url + addDetachedPromise(gadget, 'ajax_' + (gadget.state.page), + handleAsyncStore(gadget, gadget.state.page)); + state_dict = { + display_step: 'display_video', + page: gadget.state.page_count + 1 + }; + state_dict['blob_state_' + gadget.state.page] = 'sending'; + return gadget.changeState(state_dict); + } + if (evt.target.className.indexOf("change-camera-btn") !== -1) { return selectMediaDevice(gadget.state.device_id, true) .push(function (device_id) { @@ -493,15 +627,44 @@ }); } + if (evt.target.className.indexOf("show-img") !== -1) { + if (gadget.detached_promise_dict.cropper) { + gadget.detached_promise_dict.cropper.cancel('Not needed anymore, as cancelled'); + } + if (gadget.detached_promise_dict.media_stream) { + gadget.detached_promise_dict.media_stream.cancel('Not needed anymore, as cancelled'); + } + + return gadget.changeState({ + display_step: 'show_picture', + page: parseInt(evt.target.getAttribute('data-page'), 10) + }); + } + throw new Error('Unhandled button: ' + evt.target.textContent); }, false, false) + ////////////////////////////////////////////////// + // Used when submitting the form + ////////////////////////////////////////////////// + .declareMethod('getContent', function () { + var gadget = this, + result = {}; + // XXX TODO: check all blob, and only return the UUID for the one in stored state + result[gadget.state.key] = JSON.stringify({ + input_value: 'XXX', + preferred_cropped_canvas_data: gadget.state.preferred_cropped_canvas_data + }); + throw new Error('not implemented getContent'); + }, {mutex: 'changestate'}) + + .declareMethod('checkValidity', function () { + // XXX TODO: check all blob, and ensure they are all: deleted, stored + // Any other state prevent to submit the form + // XXX if the state is required, ensure there is at least one blob stored + return false; + }, {mutex: 'changestate'}) - .declareAcquiredMethod( - "submitDialogWithCustomDialogMethod", - "submitDialogWithCustomDialogMethod" - ) - .declareAcquiredMethod("getTranslationList", "getTranslationList") - .declareAcquiredMethod("notifySubmitted", "notifySubmitted"); + .declareAcquiredMethod("getTranslationList", "getTranslationList"); }(rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap)); \ No newline at end of file diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 01aac105a0e..b5c19e35071 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.21579.36694.13363 + 981.21983.29656.41591 state @@ -262,7 +262,7 @@ - 1579684586.42 + 1579708838.71 UTC -- 2.30.9 From 09f7c9b237245bb39f64abc68d7cd2855e25f599 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 27 Jan 2020 14:02:32 +0000 Subject: [PATCH 04/42] erp5_document_scanner: Avoid text editors complaining that we are using reserved word --- .../rjs_gadget_document_scanner_js.js | 44 +++++++++---------- .../rjs_gadget_document_scanner_js.xml | 4 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index f350fcba537..0fe766b5364 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -246,7 +246,7 @@ // Do not allow to show again the current image // or do not allow to show sending image (to simplify button management) disabled: (i === gadget.state.page) || (gadget.state['blob_state_' + i] === 'sending'), - class: 'show-img', + 'class': 'show-img', 'data-page': i })); } @@ -258,7 +258,7 @@ text: 'New', // Do not allow to show again the current image disabled: (len === gadget.state.page - 1), - class: 'new-btn' + 'class': 'new-btn' })); return domsugar('ol', thumbnail_dom_list); @@ -301,7 +301,7 @@ .push(function (result_list) { var button_list = [ domsugar('button', {type: 'button', - class: 'take-picture-btn ui-btn-icon-left ui-icon-circle', + 'class': 'take-picture-btn ui-btn-icon-left ui-icon-circle', text: result_list[1][0] }) ], @@ -310,21 +310,21 @@ if (result_list[0].length > 1) { button_list.push( domsugar('button', {type: 'button', - class: 'change-camera-btn ui-icon-refresh ui-btn-icon-left', + 'class': 'change-camera-btn ui-icon-refresh ui-btn-icon-left', text: result_list[1][1] }) ); } - div = domsugar('div', {class: 'camera'}, [ - domsugar('div', {class: 'camera-header'}, [ + div = domsugar('div', {'class': 'camera'}, [ + domsugar('div', {'class': 'camera-header'}, [ domsugar('h4', [ 'Page ', - domsugar('label', {class: 'page-number', text: gadget.state.page}) + domsugar('label', {'class': 'page-number', text: gadget.state.page}) ]) ]), - domsugar('div', {class: 'camera-input'}, [video]), - domsugar('div', {class: 'edit-picture'}, button_list), + domsugar('div', {'class': 'camera-input'}, [video]), + domsugar('div', {'class': 'edit-picture'}, button_list), buildPreviousThumbnailDom(gadget) ]); @@ -357,7 +357,7 @@ var // blob_url = URL.createObjectURL(blob), // img = domsugar('img', {src: blob_url}); bitmap = result_list[1], - canvas = domsugar('canvas', {class: 'canvas'}), + canvas = domsugar('canvas', {'class': 'canvas'}), defer = RSVP.defer(); // Prepare the cropper canvas @@ -365,21 +365,21 @@ canvas.height = bitmap.height; canvas.getContext('2d').drawImage(bitmap, 0, 0); - div = domsugar('div', {class: 'camera'}, [ - domsugar('div', {class: 'camera-header'}, [ + div = domsugar('div', {'class': 'camera'}, [ + domsugar('div', {'class': 'camera-header'}, [ domsugar('h4', [ 'Page ', - domsugar('label', {class: 'page-number', text: gadget.state.page}) + domsugar('label', {'class': 'page-number', text: gadget.state.page}) ]) ]), canvas, - domsugar('div', {class: 'edit-picture'}, [ + domsugar('div', {'class': 'edit-picture'}, [ domsugar('button', {type: 'button', - class: 'reset-btn ui-btn-icon-left ui-icon-times', + 'class': 'reset-btn ui-btn-icon-left ui-icon-times', text: result_list[0][0] }), domsugar('button', {type: 'button', - class: 'confirm-btn ui-btn-icon-left ui-icon-check', + 'class': 'confirm-btn ui-btn-icon-left ui-icon-check', text: result_list[0][1] }) ]), @@ -407,7 +407,7 @@ var button_list = [ // XXX TODO: improve icon domsugar('button', {type: 'button', - class: 'delete-btn ui-btn-icon-left ui-icon-times', + 'class': 'delete-btn ui-btn-icon-left ui-icon-times', text: translation_list[0] }) ], @@ -417,22 +417,22 @@ button_list.push( // XXX TODO improve icon domsugar('button', {type: 'button', - class: 'retry-btn ui-btn-icon-left ui-icon-times', + 'class': 'retry-btn ui-btn-icon-left ui-icon-times', text: translation_list[1] }) ); } - div = domsugar('div', {class: 'camera'}, [ - domsugar('div', {class: 'camera-header'}, [ + div = domsugar('div', {'class': 'camera'}, [ + domsugar('div', {'class': 'camera-header'}, [ domsugar('h4', [ 'Page ', - domsugar('label', {class: 'page-number', text: gadget.state.page + 1}) + domsugar('label', {'class': 'page-number', text: gadget.state.page + 1}) ]) ]), domsugar('img', {src: gadget.state['blob_url_' + gadget.state.page]}), // XXX TODO: why is the button rendering different from the other pages? - domsugar('div', {class: 'edit-picture'}, button_list), + domsugar('div', {'class': 'edit-picture'}, button_list), buildPreviousThumbnailDom(gadget) ]); diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index b5c19e35071..dc8ebc66ed5 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.21983.29656.41591 + 981.29059.2195.37614 state @@ -262,7 +262,7 @@ - 1579708838.71 + 1580133331.88 UTC -- 2.30.9 From d99d6dc381e8394df46b74b578bb4da5c7f8f54c Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 27 Jan 2020 16:26:08 +0000 Subject: [PATCH 05/42] erp5_document_scanner: Fix the "Change Camera" action If device_id is propagated to onStateChange, it means we have a new value. Then, check if we have this property is enough to switch --- .../web_page_module/rjs_gadget_document_scanner_js.js | 2 +- .../web_page_module/rjs_gadget_document_scanner_js.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 0fe766b5364..2b659386762 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -503,7 +503,7 @@ } else { display_step = modification_dict.display_step; } - if (display_step === 'display_video') { + if (display_step === 'display_video' || modification_dict.hasOwnProperty('device_id')) { return renderVideoCapture(gadget); } if (display_step === 'crop_picture') { diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index dc8ebc66ed5..e3633ab039c 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.29059.2195.37614 + 981.29206.2173.6297 state @@ -262,7 +262,7 @@ - 1580133331.88 + 1580142156.11 UTC -- 2.30.9 From b34fd545408b58a20e6cc8d7652e214e8ef6f65f Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 27 Jan 2020 20:40:21 +0000 Subject: [PATCH 06/42] erp5_document_scanner: Add a tales expression as default field value The default value is a JSON containing 2 keys: - the relative_url of an active process - the list of active process image content to aggregate By default, when the field is rendered for the first time, this tales expression is evaluated and a new active process is created at that point. So, default json is like: {active_process: 'portal_activities/1234', image_list: []} * store the active_process relative_url in the gadget state in the render method * store the image_list in the gadget state in the render_method * change getContent method to send a json build from the active_process and image_list state --- .../rjs_gadget_document_scanner_js.js | 9 ++- .../rjs_gadget_document_scanner_js.xml | 4 +- .../Base_getDefaultBackendDataAsJSON.py | 8 +++ .../Base_getDefaultBackendDataAsJSON.xml | 62 +++++++++++++++++++ .../your_document_scanner_gadget.xml | 34 ++++++++-- 5 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.xml diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 2b659386762..9fa31cb9eaf 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -475,11 +475,14 @@ // This method is called during the ERP5 form rendering // changeState is used to ensure not resetting the gadget current display // if not needed - var gadget = this; + var gadget = this, + default_value = JSON.parse(options.value); return selectMediaDevice(gadget.state.device_id, false) .push(function (device_id) { return gadget.changeState({ dialog_method: options.dialog_method, + active_process: default_value.active_process, + image_list: default_value.image_list, preferred_cropped_canvas_data: JSON.parse(options.preferred_cropped_canvas_data), device_id: device_id, key: options.key, @@ -651,6 +654,10 @@ var gadget = this, result = {}; // XXX TODO: check all blob, and only return the UUID for the one in stored state + result.data_json = JSON.stringify({ + active_process: gadget.state.active_process, + image_list: gadget.state.image_list + }); result[gadget.state.key] = JSON.stringify({ input_value: 'XXX', preferred_cropped_canvas_data: gadget.state.preferred_cropped_canvas_data diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index e3633ab039c..78f0c5538c4 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.29206.2173.6297 + 981.29376.40278.19438 state @@ -262,7 +262,7 @@ - 1580142156.11 + 1580157393.77 UTC diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py new file mode 100644 index 00000000000..4935ae8fb18 --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py @@ -0,0 +1,8 @@ +import json +context.log("Called") +active_process = context.getPortalObject().portal_activities.newActiveProcess() + +return json.dumps({ + "active_process": active_process.getRelativeUrl(), + "image_list": [] +}) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.xml new file mode 100644 index 00000000000..6ecf8f8573d --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.xml @@ -0,0 +1,62 @@ + + + + + + + + + + Script_magic + 3 + + + _bind_names + + + + + + + + + + _asgns + + + + name_container + container + + + name_context + context + + + name_m_self + script + + + name_subpath + traverse_subpath + + + + + + + + + + + _params + + + + id + Base_getDefaultBackendDataAsJSON + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml index 6b2b2c6e927..6864e68a184 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml @@ -10,6 +10,7 @@ delegated_list + default gadget_url renderjs_extra title @@ -51,6 +52,12 @@ tales + + default + + AAAAAAAAAAI= + + field_id @@ -62,19 +69,19 @@ gadget_url - AAAAAAAAAAI= + AAAAAAAAAAM= renderjs_extra - AAAAAAAAAAM= + AAAAAAAAAAQ= title - AAAAAAAAAAQ= + AAAAAAAAAAU= @@ -88,6 +95,10 @@ values + + default + + field_id my_gadget_field @@ -128,7 +139,7 @@ _text - string: gadget_document_scanner.html + python: here.Base_getDefaultBackendDataAsJSON() @@ -141,12 +152,25 @@ _text - python: [(\'dialog_method\', \'Base_storeDocumentFromCameraInActiveProcess\'), (\'preferred_cropped_canvas_data\', context.Base_getPreferredCropperSettingsFromSelection()),] + string: gadget_document_scanner.html + + + + + + + _text + python: [(\'dialog_method\', \'Base_storeDocumentFromCameraInActiveProcess\'), (\'preferred_cropped_canvas_data\', context.Base_getPreferredCropperSettingsFromSelection()),] + + + + + -- 2.30.9 From 51a314100f54d85628d91c2877aacec837b1a730 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 27 Jan 2020 21:20:09 +0000 Subject: [PATCH 07/42] erp5_document_scanner: Improve checkValidity method from scannerjs Change the gadget checkValidity method to return false when: - no thumbnail has been generated - a thumbnail upload is in failed state --- .../rjs_gadget_document_scanner_js.js | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 9fa31cb9eaf..0ccb56f0e00 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -666,10 +666,25 @@ }, {mutex: 'changestate'}) .declareMethod('checkValidity', function () { - // XXX TODO: check all blob, and ensure they are all: deleted, stored - // Any other state prevent to submit the form - // XXX if the state is required, ensure there is at least one blob stored - return false; + var gadget = this, + has_thumbnail = false, + key; + for (key in gadget.state) { + if (gadget.state.hasOwnProperty(key)) { + if (key.indexOf("blob_state_") !== -1 && + !gadget.state[key].match("deleted|stored")) { + return false; + } + if (key.indexOf("blob_url_") !== -1) { + if (!gadget.state[key]) { + return false; + } else if (has_thumbnail) { + has_thumbnail = true; + } + } + } + } + return has_thumbnail; }, {mutex: 'changestate'}) .declareAcquiredMethod("getTranslationList", "getTranslationList"); -- 2.30.9 From 97c9eae2e4c771b5286f6e14a4ba1a795f575731 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 27 Jan 2020 22:04:06 +0000 Subject: [PATCH 08/42] erp5_document_scanner: POST image cropped to backend * change the `extra renderjs` parameters list to pass a Python script to call when a new image is cropped. * change the gadget to use jio.util.ajax with the script full url (provided by the server) --- .../rjs_gadget_document_scanner_js.js | 33 +++++----- .../rjs_gadget_document_scanner_js.xml | 4 +- .../Base_storeNewImageCropped.py | 1 + .../Base_storeNewImageCropped.xml | 62 +++++++++++++++++++ .../your_document_scanner_gadget.xml | 2 +- 5 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.xml diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 0ccb56f0e00..55f3f20adbe 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -1,6 +1,6 @@ /*jslint indent: 2, unparam: true */ -/*global rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap*/ -(function (rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap) { +/*global rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap, FormData*/ +(function (rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap, FormData) { "use strict"; ////////////////////////////////////////////////// @@ -78,23 +78,19 @@ } function handleAsyncStore(gadget, blob_page) { + var data = new FormData(); + data.append("input_value", + gadget.state['blob_url_' + blob_page].split(';')[1].split(',')[1]); return new RSVP.Queue() .push(function () { - // XXX TODO: jio.util.ajax with - /* - JSON.stringify({ - input_value: gadget.state.blob_url_XXX.split(';')[1].split(',')[1], - // preferred_cropped_canvas_data: gadget.state.preferred_cropped_canvas_data - }) - */ - function getRandomInt(max) { - return Math.floor(Math.random() * Math.floor(max)); - } - // XXX long or not, working or not, who knows? - return RSVP.any([ - RSVP.delay(2000 + getRandomInt(3000)), - RSVP.timeout(2000 + getRandomInt(3000)) - ]); + return jIO.util.ajax({ + "type": "POST", + "url": gadget.state.store_new_image_cropped_method, + "data": data, + "xhrFields": { + withCredentials: true + } + }); }) .push(function () { var state_dict = {}; @@ -481,6 +477,7 @@ .push(function (device_id) { return gadget.changeState({ dialog_method: options.dialog_method, + store_new_image_cropped_method: options.store_new_image_cropped_method, active_process: default_value.active_process, image_list: default_value.image_list, preferred_cropped_canvas_data: JSON.parse(options.preferred_cropped_canvas_data), @@ -689,4 +686,4 @@ .declareAcquiredMethod("getTranslationList", "getTranslationList"); -}(rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap)); \ No newline at end of file +}(rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap, FormData)); \ No newline at end of file diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 78f0c5538c4..5ecb005fe82 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.29376.40278.19438 + 981.29544.47237.55466 state @@ -262,7 +262,7 @@ - 1580157393.77 + 1580162507.4 UTC diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py new file mode 100644 index 00000000000..3df2b5fc5cb --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py @@ -0,0 +1 @@ +return context.REQUEST diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.xml new file mode 100644 index 00000000000..c3bc27d0469 --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.xml @@ -0,0 +1,62 @@ + + + + + + + + + + Script_magic + 3 + + + _bind_names + + + + + + + + + + _asgns + + + + name_container + container + + + name_context + context + + + name_m_self + script + + + name_subpath + traverse_subpath + + + + + + + + + + + _params + **kw + + + id + Base_storeNewImageCropped + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml index 6864e68a184..4eebff2b607 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml @@ -165,7 +165,7 @@ _text - python: [(\'dialog_method\', \'Base_storeDocumentFromCameraInActiveProcess\'), (\'preferred_cropped_canvas_data\', context.Base_getPreferredCropperSettingsFromSelection()),] + python: [(\'dialog_method\', \'Base_storeDocumentFromCameraInActiveProcess\'), (\'preferred_cropped_canvas_data\', context.Base_getPreferredCropperSettingsFromSelection()), ("store_new_image_cropped_method", \'Base_storeNewImageCropped\')] -- 2.30.9 From 020a9cf2910c781bd58bc937cf21abeb65243ab2 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 27 Jan 2020 22:22:25 +0000 Subject: [PATCH 09/42] fixup! erp5_document_scanner: Improve checkValidity method from scannerjs make jslint happy --- .../web_page_module/rjs_gadget_document_scanner_js.js | 3 ++- .../web_page_module/rjs_gadget_document_scanner_js.xml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 55f3f20adbe..67cb205ddb4 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -675,7 +675,8 @@ if (key.indexOf("blob_url_") !== -1) { if (!gadget.state[key]) { return false; - } else if (has_thumbnail) { + } + if (gadget.state[key] && !has_thumbnail) { has_thumbnail = true; } } diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 5ecb005fe82..0b3d924468a 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.29544.47237.55466 + 981.29551.59362.28552 state @@ -262,7 +262,7 @@ - 1580162507.4 + 1580163643.67 UTC -- 2.30.9 From ef560377fcf33b57474df795fe17eb99fb47fbf4 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Tue, 28 Jan 2020 14:04:53 +0000 Subject: [PATCH 10/42] erp5_document_scanner: Add active process url to request body Request body should contain the image data and the active process relative url (depending on which erp5 context the script is called) --- .../web_page_module/rjs_gadget_document_scanner_js.js | 1 + .../web_page_module/rjs_gadget_document_scanner_js.xml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 67cb205ddb4..63c7137c70d 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -81,6 +81,7 @@ var data = new FormData(); data.append("input_value", gadget.state['blob_url_' + blob_page].split(';')[1].split(',')[1]); + data.append("active_process_url", gadget.state.active_process); return new RSVP.Queue() .push(function () { return jIO.util.ajax({ diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 0b3d924468a..6f115c8bf6a 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.29551.59362.28552 + 981.30481.36256.38348 state @@ -262,7 +262,7 @@ - 1580163643.67 + 1580220211.01 UTC -- 2.30.9 From 011972b092756c8b785798553ad9652f6da8fe82 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Tue, 28 Jan 2020 15:28:54 +0000 Subject: [PATCH 11/42] erp5_document_scanner: Response body may contain a local reference of the uploaded image The http response should return a 200 response in case of success. The response body may contain a local reference of the uploaded image --- .../Base_postDataToActiveResult.py | 16 +++++++++++++--- ...ase_storeDocumentFromCameraInActiveProcess.py | 2 +- .../Base_storeNewImageCropped.py | 6 +++++- .../Base_storeNewImageCropped.xml | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py index 19b37877ad0..a78f0268ba5 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py @@ -2,6 +2,17 @@ We need proy role as manager to create a new active process and post active result """ +import string +import random + +reference = str( + DateTime().millis()) + '-' + ''.join( + random.sample( + string.letters+string.digits, random.randint( + 6, 10) + ) +) + portal = context.getPortalObject() if REQUEST: @@ -12,6 +23,5 @@ if active_process_url: else: active_process = portal.portal_activities.newActiveProcess() -active_process.postActiveResult(detail=detail) - -return active_process +active_process.postActiveResult(detail=detail, reference=reference) +return active_process, reference diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeDocumentFromCameraInActiveProcess.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeDocumentFromCameraInActiveProcess.py index d39242917ec..2ff67f187c3 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeDocumentFromCameraInActiveProcess.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeDocumentFromCameraInActiveProcess.py @@ -30,7 +30,7 @@ if not image_str: return context.Base_renderForm('Base_viewUploadDocumentFromCameraDialog', message=translateString('Nothing to capture')) -active_process = context.Base_postDataToActiveResult( +active_process, _ = context.Base_postDataToActiveResult( active_process_url, image_str) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py index 3df2b5fc5cb..b3b74e90e9c 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py @@ -1 +1,5 @@ -return context.REQUEST +import json + +_, reference = context.Base_postDataToActiveResult( + active_process_url, input_value) +return json.dumps({"reference": reference}) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.xml index c3bc27d0469..711192e99d6 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.xml @@ -50,7 +50,7 @@ _params - **kw + input_value, active_process_url, **kw id -- 2.30.9 From 8cfee10bf6478e83bdc4e7bb43133f50f253f819 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Wed, 29 Jan 2020 13:12:15 +0000 Subject: [PATCH 12/42] erp5_document_scanner: Clean up --- .../rjs_gadget_document_scanner_css.xml | 4 ++-- .../rjs_gadget_document_scanner_html.html | 21 ------------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml index 4f87c3300b9..4c6e6c163ce 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml @@ -244,7 +244,7 @@ serial - 981.19198.64390.32238 + 981.31881.58567.1228 state @@ -262,7 +262,7 @@ - 1579541862.76 + 1580302701.45 UTC diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html index 83d6a1d27c4..96cbf7cb494 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html @@ -20,26 +20,5 @@
- \ No newline at end of file -- 2.30.9 From 2a5410de147b7cffba96f7269dc50e22612564aa Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Wed, 29 Jan 2020 13:30:28 +0000 Subject: [PATCH 13/42] erp5_document_scanner: Prevent this CSS to impact the other page elements Clean up and Change all gadget's CSS rules to be prefixed by `div[data-gadget-url$="gadget_document_scanner.html"]` --- .../rjs_gadget_document_scanner_css.css | 76 ++++++------------- .../rjs_gadget_document_scanner_html.xml | 4 +- 2 files changed, 26 insertions(+), 54 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css index b56163ec456..b96f546653a 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css @@ -1,13 +1,11 @@ -.device-selector { +div[data-gadget-url$="gadget_document_scanner.html"] { text-align: center; - font-size: 19px; } -div[data-gadget-scope="field_your_document_scanner_gadget"] { - text-align: center; -} - -.video, .photo, .camera-output, .canvas { +div[data-gadget-url$="gadget_document_scanner.html"] .photo, +div[data-gadget-url$="gadget_document_scanner.html"] .video, +div[data-gadget-url$="gadget_document_scanner.html"] .camera-output, +div[data-gadget-url$="gadget_document_scanner.html"] .canvas { max-width: 100%; width: auto; max-height: 500px; @@ -15,45 +13,38 @@ div[data-gadget-scope="field_your_document_scanner_gadget"] { text-align: center; } -.camera-input, .camera-output { +div[data-gadget-url$="gadget_document_scanner.html"] .camera-input { min-height: 360px; - // display: none; } -.canvas { - // display: none; - filter: brightness(1); -} - -.page-number { +div[data-gadget-url$="gadget_document_scanner.html"] .page-number { display: inline; } -.camera-header { +div[data-gadget-url$="gadget_document_scanner.html"] .camera-header { font-size: 12pt; font-weight: 400; } -.camera-input, .camera-output, .camera-header, .edit-picture { +div[data-gadget-url$="gadget_document_scanner.html"] .camera-input, +div[data-gadget-url$="gadget_document_scanner.html"] .camera-output, +div[data-gadget-url$="gadget_document_scanner.html"] .camera-header, +div[data-gadget-url$="gadget_document_scanner.html"] .edit-picture { text-align: center; } -.camera, .camera-input, .camera-output { +div[data-gadget-url$="gadget_document_scanner.html"] .camera, +div[data-gadget-url$="gadget_document_scanner.html"] .camera-input { display: inline-block; } -.capture-button, .reset-button { - display: inline; - margin: 0 2em 0 2em; -} - -.startbutton { - display: block; - margin: 0 auto; -} - -.reset-btn, .confirm-btn, .edit-btn, .take-picture-btn, - .capture-btn, .change-camera-btn, .confirm-btn { +div[data-gadget-url$="gadget_document_scanner.html"] .reset-btn, +div[data-gadget-url$="gadget_document_scanner.html"] .confirm-btn, +div[data-gadget-url$="gadget_document_scanner.html"] .edit-btn, +div[data-gadget-url$="gadget_document_scanner.html"] .take-picture-btn, +div[data-gadget-url$="gadget_document_scanner.html"] .capture-btn, +div[data-gadget-url$="gadget_document_scanner.html"] .change-camera-btn, +div[data-gadget-url$="gadget_document_scanner.html"] .confirm-btn { color: #212529; padding: 3pt; border: 1px solid rgba(0, 0, 0, 0.14); @@ -62,30 +53,11 @@ div[data-gadget-scope="field_your_document_scanner_gadget"] { margin-right: 6pt; } -.ui-btn-icon-left:before { +div[data-gadget-url$="gadget_document_scanner.html"] .ui-btn-icon-left:before { margin-right: 6pt; } -/* -.take-picture-btn, .capture-btn, .confirm-btn, - .reset-btn, .confirm-btn, .change-camera-btn, .edit-btn { - display: none; -} -*/ - -.contentarea { - font-size: 16px; - font-family: "Lucida Grande", "Arial", sans-serif; - width: 760px; -} - -button:disabled, -button[disabled]{ +div[data-gadget-url$="gadget_document_scanner.html"] button:disabled, +div[data-gadget-url$="gadget_document_scanner.html"] button[disabled] { color: #999999; -} - -@media only screen and (max-width: 600px) { - body { - max-height: 360px; - } } \ No newline at end of file diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml index db8e9b7cfd3..d76a858a9dc 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml @@ -242,7 +242,7 @@
serial - 981.18960.58948.16110 + 981.22396.11562.3498 state @@ -260,7 +260,7 @@ - 1579527559.65 + 1580300337.44 UTC -- 2.30.9 From ff5255d5369c09fbc81c5924a84b8dcff1574cf6 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Wed, 29 Jan 2020 15:31:08 +0000 Subject: [PATCH 14/42] erp5_document_scanner: Improve texts of all buttons --- .../rjs_gadget_document_scanner_js.js | 14 +++++++------- .../rjs_gadget_document_scanner_js.xml | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 63c7137c70d..c81b2108904 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -95,7 +95,7 @@ }) .push(function () { var state_dict = {}; - state_dict['blob_state_' + blob_page] = 'stored'; + state_dict['blob_state_' + blob_page] = 'OK'; // XXX TODO: ajax must return a active process image content UUID // which should be sent in the final form submittion state_dict['blob_uuid_' + blob_page] = 'XXX'; @@ -103,7 +103,7 @@ }, function () { // XXX TODO: Handle error case var state_dict = {}; - state_dict['blob_state_' + blob_page] = 'failed'; + state_dict['blob_state_' + blob_page] = 'Error'; return gadget.changeState(state_dict); }); } @@ -239,7 +239,7 @@ // XXX TODO display a loader when sending if (gadget.state['blob_state_' + i] !== 'deleted') { thumbnail_dom_list.push(domsugar('button', {type: 'button', - text: 'Image' + (i + 1) + ' (' + gadget.state['blob_state_' + i] + ')', + text: 'Page' + (i + 1) + ' (' + gadget.state['blob_state_' + i] + ')', // Do not allow to show again the current image // or do not allow to show sending image (to simplify button management) disabled: (i === gadget.state.page) || (gadget.state['blob_state_' + i] === 'sending'), @@ -252,7 +252,7 @@ // Always add a button to generate a new image // XXX TODO translation + right term thumbnail_dom_list.push(domsugar('button', {type: 'button', - text: 'New', + text: 'New Page', // Do not allow to show again the current image disabled: (len === gadget.state.page - 1), 'class': 'new-btn' @@ -292,7 +292,7 @@ video.play(); return RSVP.all([ getVideoDeviceList(), - gadget.getTranslationList(["Take Picture", "Change Camera"]) + gadget.getTranslationList(["Capture", "Change Camera"]) ]); }) .push(function (result_list) { @@ -346,7 +346,7 @@ .push(function (blob) { gadget.detached_promise_dict.media_stream.cancel('Not needed anymore, as captured'); return RSVP.all([ - gadget.getTranslationList(["Reset", "Confirm"]), + gadget.getTranslationList(["Delete", "Save"]), createImageBitmap(blob) ]); }) @@ -399,7 +399,7 @@ } function renderSubmittedPicture(gadget) { - return gadget.getTranslationList(["Delete", "Retry"]) + return gadget.getTranslationList(["Delete", "Save"]) .push(function (translation_list) { var button_list = [ // XXX TODO: improve icon diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 6f115c8bf6a..2ad83f6c219 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.30481.36256.38348 + 981.30507.33878.33041 state @@ -262,7 +262,7 @@ - 1580220211.01 + 1580308941.19 UTC -- 2.30.9 From a53838f57011bcee2c831f178ea0e6c1c4bde76b Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Wed, 29 Jan 2020 20:00:23 +0000 Subject: [PATCH 15/42] erp5_document_scanner: Remove useless debug --- .../erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py index 4935ae8fb18..6f8e9065fbf 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py @@ -1,5 +1,5 @@ import json -context.log("Called") + active_process = context.getPortalObject().portal_activities.newActiveProcess() return json.dumps({ -- 2.30.9 From b3d3954d5aedeb37789a942c3617f355a7cc81fe Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Thu, 30 Jan 2020 02:57:56 +0000 Subject: [PATCH 16/42] erp5_document_scanner: update i18n info --- .../rjs_gadget_document_scanner_html.html | 11 +++++++---- .../rjs_gadget_document_scanner_html.xml | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html index 96cbf7cb494..03f0fda25c4 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html @@ -3,10 +3,13 @@ diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml index d76a858a9dc..d2eb3e76bcb 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.xml @@ -242,7 +242,7 @@ serial - 981.22396.11562.3498 + 981.31842.62752.52411 state @@ -260,7 +260,7 @@ - 1580300337.44 + 1580352868.23 UTC -- 2.30.9 From b312601dcff69730735830f2da6e57b73f9641b1 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Fri, 31 Jan 2020 13:22:43 +0000 Subject: [PATCH 17/42] erp5_document_scanner: Translate all texts To translate all texts, we added more promises and queues to make sure everything works as expected --- .../rjs_gadget_document_scanner_js.js | 93 +++++++++++-------- .../rjs_gadget_document_scanner_js.xml | 4 +- 2 files changed, 55 insertions(+), 42 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index c81b2108904..c10d7b70526 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -232,33 +232,36 @@ var i, len = gadget.state.page_count, thumbnail_dom_list = []; - for (i = 0; i < len; i += 1) { - // XXX TODO: show nice looking thumbnail - // from gadget.state.blob_url_i - // XXX TODO translation + right term - // XXX TODO display a loader when sending - if (gadget.state['blob_state_' + i] !== 'deleted') { - thumbnail_dom_list.push(domsugar('button', {type: 'button', - text: 'Page' + (i + 1) + ' (' + gadget.state['blob_state_' + i] + ')', - // Do not allow to show again the current image - // or do not allow to show sending image (to simplify button management) - disabled: (i === gadget.state.page) || (gadget.state['blob_state_' + i] === 'sending'), - 'class': 'show-img', - 'data-page': i - })); - } - } - // Always add a button to generate a new image - // XXX TODO translation + right term - thumbnail_dom_list.push(domsugar('button', {type: 'button', - text: 'New Page', - // Do not allow to show again the current image - disabled: (len === gadget.state.page - 1), - 'class': 'new-btn' - })); - return domsugar('ol', thumbnail_dom_list); + return gadget.getTranslationList(["Page", "New Page"]) + .push(function (result_list) { + for (i = 0; i < len; i += 1) { + // XXX TODO: show nice looking thumbnail + // from gadget.state.blob_url_i + // XXX TODO translation + right term + // XXX TODO display a loader when sending + if (gadget.state['blob_state_' + i] !== 'deleted') { + thumbnail_dom_list.push(domsugar('button', {type: 'button', + text: result_list[0] + (i + 1) + ' (' + gadget.state['blob_state_' + i] + ')', + // Do not allow to show again the current image + // or do not allow to show sending image (to simplify button management) + disabled: (i === gadget.state.page) || (gadget.state['blob_state_' + i] === 'sending'), + 'class': 'show-img', + 'data-page': i + })); + } + } + // Always add a button to generate a new image + // XXX TODO translation + right term + thumbnail_dom_list.push(domsugar('button', {type: 'button', + text: result_list[1], + // Do not allow to show again the current image + disabled: (len === gadget.state.page - 1), + 'class': 'new-btn' + })); + return domsugar('ol', thumbnail_dom_list); + }); } // Display the video stream from a media source @@ -292,7 +295,8 @@ video.play(); return RSVP.all([ getVideoDeviceList(), - gadget.getTranslationList(["Capture", "Change Camera"]) + gadget.getTranslationList(["Capture", "Change Camera"]), + buildPreviousThumbnailDom(gadget) ]); }) .push(function (result_list) { @@ -322,7 +326,7 @@ ]), domsugar('div', {'class': 'camera-input'}, [video]), domsugar('div', {'class': 'edit-picture'}, button_list), - buildPreviousThumbnailDom(gadget) + result_list[2] ]); gadget.element.replaceChild(div, gadget.element.firstElementChild); @@ -347,7 +351,8 @@ gadget.detached_promise_dict.media_stream.cancel('Not needed anymore, as captured'); return RSVP.all([ gadget.getTranslationList(["Delete", "Save"]), - createImageBitmap(blob) + createImageBitmap(blob), + buildPreviousThumbnailDom(gadget) ]); }) .push(function (result_list) { @@ -380,7 +385,7 @@ text: result_list[0][1] }) ]), - buildPreviousThumbnailDom(gadget) + result_list[2] ]); // XXX How to change the dom only when cropper is ready? @@ -399,13 +404,19 @@ } function renderSubmittedPicture(gadget) { - return gadget.getTranslationList(["Delete", "Save"]) - .push(function (translation_list) { + return new RSVP.Queue() + .push(function () { + return RSVP.all([ + gadget.getTranslationList(["Delete", "Save"]), + buildPreviousThumbnailDom(gadget) + ]); + }) + .push(function (result_list) { var button_list = [ // XXX TODO: improve icon domsugar('button', {type: 'button', 'class': 'delete-btn ui-btn-icon-left ui-icon-times', - text: translation_list[0] + text: result_list[0][0] }) ], div; @@ -415,7 +426,7 @@ // XXX TODO improve icon domsugar('button', {type: 'button', 'class': 'retry-btn ui-btn-icon-left ui-icon-times', - text: translation_list[1] + text: result_list[0][1] }) ); } @@ -430,7 +441,7 @@ domsugar('img', {src: gadget.state['blob_url_' + gadget.state.page]}), // XXX TODO: why is the button rendering different from the other pages? domsugar('div', {'class': 'edit-picture'}, button_list), - buildPreviousThumbnailDom(gadget) + result_list[1] ]); // XXX How to change the dom only when cropper is ready? @@ -521,12 +532,14 @@ // Only refresh the thumbnail list // if display_step is not modified // XXX TODO use a more precise selector - thumbnail_container = gadget.element.querySelector('ol'); - thumbnail_container.parentElement.replaceChild( - buildPreviousThumbnailDom(gadget), - thumbnail_container - ); - + return buildPreviousThumbnailDom(gadget) + .push(function (result) { + thumbnail_container = gadget.element.querySelector('ol'); + thumbnail_container.parentElement.replaceChild( + result, + thumbnail_container + ); + }); }) .onEvent("click", function (evt) { diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 2ad83f6c219..c7b2fa91171 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.30507.33878.33041 + 981.34772.33248.46728 state @@ -262,7 +262,7 @@ - 1580308941.19 + 1580476795.66 UTC -- 2.30.9 From 6b64127ec6f612c5221287fd730b61431ebcbffe Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Fri, 31 Jan 2020 15:21:58 +0000 Subject: [PATCH 18/42] erp5_document_scanner: Update TODO lists --- .../web_page_module/rjs_gadget_document_scanner_js.js | 4 ++-- .../web_page_module/rjs_gadget_document_scanner_js.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index c10d7b70526..2f3db98e7bb 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -239,7 +239,7 @@ for (i = 0; i < len; i += 1) { // XXX TODO: show nice looking thumbnail // from gadget.state.blob_url_i - // XXX TODO translation + right term + // XXX right term // XXX TODO display a loader when sending if (gadget.state['blob_state_' + i] !== 'deleted') { thumbnail_dom_list.push(domsugar('button', {type: 'button', @@ -253,7 +253,7 @@ } } // Always add a button to generate a new image - // XXX TODO translation + right term + // XXX TODO right term thumbnail_dom_list.push(domsugar('button', {type: 'button', text: result_list[1], // Do not allow to show again the current image diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index c7b2fa91171..253c8806d21 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.34772.33248.46728 + 981.34783.60808.5171 state @@ -262,7 +262,7 @@ - 1580476795.66 + 1580484058.37 UTC -- 2.30.9 From 0833af7161383a0849865f9892098950f77793b3 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Fri, 31 Jan 2020 15:25:31 +0000 Subject: [PATCH 19/42] erp5_document_scanner: Remove not used funcionts We will handle contrast differently --- .../rjs_gadget_document_scanner_js.js | 51 ------------------- .../rjs_gadget_document_scanner_js.xml | 4 +- 2 files changed, 2 insertions(+), 53 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 2f3db98e7bb..c19fd3996a2 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -111,57 +111,6 @@ ////////////////////////////////////////////////// // helper function ////////////////////////////////////////////////// - /*function contrastImage(input, output, contrast) { - var i, - outputContext, - inputContext = input.getContext("2d"), - imageData = inputContext.getImageData(0, 0, input.width, input.height), - data = imageData.data, - factor = (259 * (contrast + 255)) / (255 * (259 - contrast)); - for (i = 0; i < data.length; i += 4) { - data[i] = factor * (data[i] - 128) + 128; - data[i + 1] = factor * (data[i + 1] - 128) + 128; - data[i + 2] = factor * (data[i + 2] - 128) + 128; - } - outputContext = output.getContext("2d"); - outputContext.putImageData(imageData, 0, 0); - }*/ - - /*function grayscale(input, output) { - var i, - gray, - outputContext, - outputCanvas = document.createElement("canvas"), - inputContext = input.getContext("2d"), - imageData = inputContext.getImageData(0, 0, input.width, input.height), - data = imageData.data, - arraylength = input.width * input.height * 4; - //gray = 0.3*R + 0.59*G + 0.11*B - // http://www.tannerhelland.com/3643/grayscale-image-algorithm-vb6/ - for (i = arraylength - 1; i > 0; i -= 4) { - gray = 0.3 * data[i - 3] + 0.59 * data[i - 2] + 0.11 * data[i - 1]; - data[i - 3] = gray; - data[i - 2] = gray; - data[i - 1] = gray; - } - outputContext = outputCanvas.getContext("2d"); - outputContext.putImageData(imageData, 0, 0); - data = canvas.toDataURL("image/png"); - output.setAttribute("src", data); - if (cropper) { - cropper.destroy(); - } - return new RSVP.Queue() - .push(function () { - cropper = new Cropper( - output, - { - data: preferred_cropped_canvas_data - } - ); - }); - }*/ - function getVideoDeviceList() { if (!navigator.mediaDevices) { throw new Error("mediaDevices is not supported"); diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 253c8806d21..ba0229d9b8d 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.34783.60808.5171 + 981.34904.63769.51387 state @@ -262,7 +262,7 @@ - 1580484058.37 + 1580484250.78 UTC -- 2.30.9 From 16e51e3954321b892d4b0a8efa9751769619bc11 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Fri, 31 Jan 2020 15:27:44 +0000 Subject: [PATCH 20/42] erp5_document_scanner: Remove TODO already done --- .../web_page_module/rjs_gadget_document_scanner_js.js | 2 -- .../web_page_module/rjs_gadget_document_scanner_js.xml | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index c19fd3996a2..100379e2849 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -558,7 +558,6 @@ return gadget.changeState(state_dict); }) .push(function () { - // XXX TODO Send the image to ERP5 // XXX Ensure that you have the active process relative url addDetachedPromise(gadget, 'ajax_' + (gadget.state.page_count - 1), handleAsyncStore(gadget, gadget.state.page_count - 1)); @@ -568,7 +567,6 @@ } if (evt.target.className.indexOf("retry-btn") !== -1) { - // XXX TODO Send the image to ERP5 // XXX Ensure that you have the active process relative url addDetachedPromise(gadget, 'ajax_' + (gadget.state.page), handleAsyncStore(gadget, gadget.state.page)); diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index ba0229d9b8d..4795c6af207 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.34904.63769.51387 + 981.34908.11780.34713 state @@ -262,7 +262,7 @@ - 1580484250.78 + 1580484433.54 UTC -- 2.30.9 From a07158d0300b59625168ab4fec1ba09f44557e36 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 3 Feb 2020 23:09:36 +0000 Subject: [PATCH 21/42] erp5_document_scanner: change the thumbnail list display to show a thumbnail for previous images. Drop the current displayed text --- .../rjs_gadget_document_scanner_css.css | 8 ++++ .../rjs_gadget_document_scanner_css.xml | 4 +- .../rjs_gadget_document_scanner_js.js | 40 +++++++++---------- .../rjs_gadget_document_scanner_js.xml | 4 +- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css index b96f546653a..2f94343c575 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css @@ -60,4 +60,12 @@ div[data-gadget-url$="gadget_document_scanner.html"] .ui-btn-icon-left:before { div[data-gadget-url$="gadget_document_scanner.html"] button:disabled, div[data-gadget-url$="gadget_document_scanner.html"] button[disabled] { color: #999999; +} + +div[data-gadget-url$="gadget_document_scanner.html"] > .camera > .thumbnail-list { + padding-top: .5em; +} + +div[data-gadget-url$="gadget_document_scanner.html"] .show-img { + width: 35%; } \ No newline at end of file diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml index 4c6e6c163ce..ff5666e0f2e 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml @@ -244,7 +244,7 @@ serial - 981.31881.58567.1228 + 981.39650.41596.57241 state @@ -262,7 +262,7 @@ - 1580302701.45 + 1580768900.5 UTC diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 100379e2849..bf03ebfad92 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -185,23 +185,6 @@ return gadget.getTranslationList(["Page", "New Page"]) .push(function (result_list) { - for (i = 0; i < len; i += 1) { - // XXX TODO: show nice looking thumbnail - // from gadget.state.blob_url_i - // XXX right term - // XXX TODO display a loader when sending - if (gadget.state['blob_state_' + i] !== 'deleted') { - thumbnail_dom_list.push(domsugar('button', {type: 'button', - text: result_list[0] + (i + 1) + ' (' + gadget.state['blob_state_' + i] + ')', - // Do not allow to show again the current image - // or do not allow to show sending image (to simplify button management) - disabled: (i === gadget.state.page) || (gadget.state['blob_state_' + i] === 'sending'), - 'class': 'show-img', - 'data-page': i - })); - } - } - // Always add a button to generate a new image // XXX TODO right term thumbnail_dom_list.push(domsugar('button', {type: 'button', text: result_list[1], @@ -209,7 +192,22 @@ disabled: (len === gadget.state.page - 1), 'class': 'new-btn' })); - return domsugar('ol', thumbnail_dom_list); + + for (i = 0; i < len; i += 1) { + // XXX right term + // XXX TODO display a loader when saving + if (gadget.state['blob_state_' + i] !== 'deleted') { + thumbnail_dom_list.push(domsugar('button', { + type: "button", + // Do not allow to show again the current image + // or do not allow to show saving image (to simplify button management) + disabled: (i === gadget.state.page) || (gadget.state['blob_state_' + i] === 'saving') + }, [domsugar("img", {"class": "show-img", + 'data-page': i, + src: gadget.state['blob_url_' + i]})])); + } + } + return domsugar('ol', {"class": "thumbnail-list"}, thumbnail_dom_list); }); } @@ -493,7 +491,7 @@ .onEvent("click", function (evt) { // Only handle click on BUTTON element - if (evt.target.tagName !== 'BUTTON') { + if (evt.target.tagName !== 'BUTTON' && evt.target.tagName !== 'IMG') { return; } @@ -554,7 +552,7 @@ }; // Keep image date, as user may need to display it again state_dict['blob_url_' + gadget.state.page_count] = evt.target.result; - state_dict['blob_state_' + gadget.state.page_count] = 'sending'; + state_dict['blob_state_' + gadget.state.page_count] = 'saving'; return gadget.changeState(state_dict); }) .push(function () { @@ -574,7 +572,7 @@ display_step: 'display_video', page: gadget.state.page_count + 1 }; - state_dict['blob_state_' + gadget.state.page] = 'sending'; + state_dict['blob_state_' + gadget.state.page] = 'saving'; return gadget.changeState(state_dict); } diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 4795c6af207..dd9ab9cc075 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.34908.11780.34713 + 981.39686.3809.47701 state @@ -262,7 +262,7 @@ - 1580484433.54 + 1580771167.45 UTC -- 2.30.9 From f3eea0b3bfb6fbf84f388319c16df3956c1f5ff4 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 3 Feb 2020 23:39:58 +0000 Subject: [PATCH 22/42] erp5_document_scanner: ajax must return a active process image content UUID --- .../web_page_module/rjs_gadget_document_scanner_js.js | 8 +++----- .../web_page_module/rjs_gadget_document_scanner_js.xml | 4 ++-- .../erp5_document_scanner/Base_storeNewImageCropped.py | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index bf03ebfad92..e4756c2fe90 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -80,7 +80,7 @@ function handleAsyncStore(gadget, blob_page) { var data = new FormData(); data.append("input_value", - gadget.state['blob_url_' + blob_page].split(';')[1].split(',')[1]); + gadget.state['blob_url_' + blob_page].split(';')[1].split(',')[1]); data.append("active_process_url", gadget.state.active_process); return new RSVP.Queue() .push(function () { @@ -93,12 +93,10 @@ } }); }) - .push(function () { + .push(function (response) { var state_dict = {}; state_dict['blob_state_' + blob_page] = 'OK'; - // XXX TODO: ajax must return a active process image content UUID - // which should be sent in the final form submittion - state_dict['blob_uuid_' + blob_page] = 'XXX'; + state_dict['blob_uuid_' + blob_page] = response.uuid; return gadget.changeState(state_dict); }, function () { // XXX TODO: Handle error case diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index dd9ab9cc075..357c4042fe8 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.39686.3809.47701 + 981.39718.56660.63334 state @@ -262,7 +262,7 @@ - 1580771167.45 + 1580773087.67 UTC diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py index b3b74e90e9c..7f5fcd8c66f 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py @@ -1,5 +1,5 @@ import json -_, reference = context.Base_postDataToActiveResult( +_, uuid = context.Base_postDataToActiveResult( active_process_url, input_value) -return json.dumps({"reference": reference}) +return json.dumps({"uuid": uuid}) -- 2.30.9 From 085fbd3bb66404d9f609855c905162fc4ba16b4e Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Tue, 4 Feb 2020 02:08:24 +0000 Subject: [PATCH 23/42] erp5_document_scanner: Remove XXX already fixed --- .../web_page_module/rjs_gadget_document_scanner_js.js | 2 -- .../web_page_module/rjs_gadget_document_scanner_js.xml | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index e4756c2fe90..b0b256524f0 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -183,7 +183,6 @@ return gadget.getTranslationList(["Page", "New Page"]) .push(function (result_list) { - // XXX TODO right term thumbnail_dom_list.push(domsugar('button', {type: 'button', text: result_list[1], // Do not allow to show again the current image @@ -192,7 +191,6 @@ })); for (i = 0; i < len; i += 1) { - // XXX right term // XXX TODO display a loader when saving if (gadget.state['blob_state_' + i] !== 'deleted') { thumbnail_dom_list.push(domsugar('button', { diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 357c4042fe8..9be816691a8 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.39718.56660.63334 + 981.39722.8382.24115 state @@ -262,7 +262,7 @@ - 1580773087.67 + 1580782043.63 UTC -- 2.30.9 From d709ca6ce8d9c462c0c8c9986661cee329976bc9 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Tue, 4 Feb 2020 16:06:01 +0000 Subject: [PATCH 24/42] erp5_document_scanner: Remove unused code --- .../web_page_module/rjs_gadget_document_scanner_js.js | 4 +--- .../web_page_module/rjs_gadget_document_scanner_js.xml | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index b0b256524f0..799e7ff49d5 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -299,9 +299,7 @@ ]); }) .push(function (result_list) { - var // blob_url = URL.createObjectURL(blob), - // img = domsugar('img', {src: blob_url}); - bitmap = result_list[1], + var bitmap = result_list[1], canvas = domsugar('canvas', {'class': 'canvas'}), defer = RSVP.defer(); diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 9be816691a8..749b332d015 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.39722.8382.24115 + 981.39871.25821.10052 state @@ -262,7 +262,7 @@ - 1580782043.63 + 1580832330.08 UTC -- 2.30.9 From 527e9c3f63f1c5260c5f3d8f94bfba8fb7717d30 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Tue, 4 Feb 2020 16:44:07 +0000 Subject: [PATCH 25/42] erp5_document_scanner: Use portal_ids to generate ids in order --- .../Base_postDataToActiveResult.py | 18 ++++++++---------- .../Base_postDataToActiveResult.xml | 2 +- .../Base_storeNewImageCropped.py | 3 ++- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py index a78f0268ba5..685dafed6cb 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py @@ -5,14 +5,6 @@ import string import random -reference = str( - DateTime().millis()) + '-' + ''.join( - random.sample( - string.letters+string.digits, random.randint( - 6, 10) - ) -) - portal = context.getPortalObject() if REQUEST: @@ -23,5 +15,11 @@ if active_process_url: else: active_process = portal.portal_activities.newActiveProcess() -active_process.postActiveResult(detail=detail, reference=reference) -return active_process, reference +if generate_new_uid: + id_group = ('document_scanner_js', active_process.getUid()) + new_uid = portal.portal_ids.generateNewId(id_group=id_group, default=0) +else: + new_uid = None + +active_process.postActiveResult(detail=detail, reference=new_uid) +return active_process, new_uid diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.xml index aebf41d96ca..35be01bdfab 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.xml @@ -50,7 +50,7 @@ _params - active_process_url, detail, REQUEST=None, **kw + active_process_url, detail, generate_new_uid=False, REQUEST=None, **kw _proxy_roles diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py index 7f5fcd8c66f..1540954c9f7 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeNewImageCropped.py @@ -1,5 +1,6 @@ import json _, uuid = context.Base_postDataToActiveResult( - active_process_url, input_value) + active_process_url, input_value, + generate_new_uid=True) return json.dumps({"uuid": uuid}) -- 2.30.9 From e7213f93928101b90aad271db6d239e7e493ae45 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Tue, 4 Feb 2020 19:53:15 +0000 Subject: [PATCH 26/42] erp5_document_scanner: mark a thumbnail in red if the upload failed --- .../rjs_gadget_document_scanner_css.css | 4 ++++ .../rjs_gadget_document_scanner_css.xml | 2 +- .../rjs_gadget_document_scanner_js.js | 14 ++++++++++---- .../rjs_gadget_document_scanner_js.xml | 4 ++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css index 2f94343c575..687abf6175f 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css @@ -68,4 +68,8 @@ div[data-gadget-url$="gadget_document_scanner.html"] > .camera > .thumbnail-list div[data-gadget-url$="gadget_document_scanner.html"] .show-img { width: 35%; +} + +div[data-gadget-url$="gadget_document_scanner.html"] .upload-error { + border: 3px solid red; } \ No newline at end of file diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml index ff5666e0f2e..69a3fc2674d 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml @@ -262,7 +262,7 @@ - 1580768900.5 + 1580843720.26 UTC diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 799e7ff49d5..f2736a207d8 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -101,7 +101,7 @@ }, function () { // XXX TODO: Handle error case var state_dict = {}; - state_dict['blob_state_' + blob_page] = 'Error'; + state_dict['blob_state_' + blob_page] = 'error'; return gadget.changeState(state_dict); }); } @@ -177,6 +177,7 @@ function buildPreviousThumbnailDom(gadget) { var i, + img_class, len = gadget.state.page_count, thumbnail_dom_list = []; @@ -193,12 +194,17 @@ for (i = 0; i < len; i += 1) { // XXX TODO display a loader when saving if (gadget.state['blob_state_' + i] !== 'deleted') { + if (gadget.state['blob_state_' + i] === "error") { + img_class = "show-img upload-error"; + } else { + img_class = "show-img"; + } thumbnail_dom_list.push(domsugar('button', { type: "button", // Do not allow to show again the current image // or do not allow to show saving image (to simplify button management) disabled: (i === gadget.state.page) || (gadget.state['blob_state_' + i] === 'saving') - }, [domsugar("img", {"class": "show-img", + }, [domsugar("img", {"class": img_class, 'data-page': i, src: gadget.state['blob_url_' + i]})])); } @@ -362,7 +368,7 @@ ], div; - if (gadget.state['blob_state_' + gadget.state.page] === 'failed') { + if (gadget.state['blob_state_' + gadget.state.page] === 'error') { button_list.push( // XXX TODO improve icon domsugar('button', {type: 'button', @@ -475,7 +481,7 @@ // XXX TODO use a more precise selector return buildPreviousThumbnailDom(gadget) .push(function (result) { - thumbnail_container = gadget.element.querySelector('ol'); + thumbnail_container = gadget.element.querySelector('.thumbnail-list'); thumbnail_container.parentElement.replaceChild( result, thumbnail_container diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index 749b332d015..e63633b2d88 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.39871.25821.10052 + 981.40903.42421.64529 state @@ -262,7 +262,7 @@ - 1580832330.08 + 1580845018.98 UTC -- 2.30.9 From 6c23652ce1028fbd56dc88cda2dc982ca869435e Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Wed, 5 Feb 2020 22:13:11 +0000 Subject: [PATCH 27/42] erp5_document_scanner: Remove field not required --- ...ase_viewUploadDocumentFromCameraDialog.xml | 1 - .../your_active_process_url.xml | 96 ------------------- 2 files changed, 97 deletions(-) delete mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_active_process_url.xml diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog.xml index 7495b0edfd0..617f4ea965f 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog.xml @@ -95,7 +95,6 @@ left - your_active_process_url your_document_scanner_gadget diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_active_process_url.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_active_process_url.xml deleted file mode 100644 index b12726a259e..00000000000 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_active_process_url.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - delegated_list - - - hidden - - - - - id - your_active_process_url - - - message_values - - - - external_validator_failed - The input failed the external validator. - - - - - - overrides - - - - field_id - - - - form_id - - - - target - - - - - - - tales - - - - field_id - - - - form_id - - - - target - - - - - - - values - - - - field_id - my_string_field - - - form_id - Base_viewFieldLibrary - - - hidden - 1 - - - target - Click to edit the target - - - - - - - - -- 2.30.9 From 794562311ba4541b3f9faad8082994348528794f Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Wed, 5 Feb 2020 22:20:23 +0000 Subject: [PATCH 28/42] erp5_document_scanner: Fix code to generate PDF from images taken --- .../rjs_gadget_document_scanner_js.js | 45 ++++++++++++------- .../rjs_gadget_document_scanner_js.xml | 4 +- .../Base_getTempImageList.py | 10 +++-- .../Base_getTempImageList.xml | 2 +- .../Base_uploadDocumentFromCamera.py | 4 +- .../Base_uploadDocumentFromCamera.xml | 2 +- ...Base_uploadDocumentFromCameraByActivity.py | 8 ++++ ...ase_uploadDocumentFromCameraByActivity.xml | 2 +- 8 files changed, 49 insertions(+), 28 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index f2736a207d8..d200ec9b0db 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -93,13 +93,13 @@ } }); }) - .push(function (response) { - var state_dict = {}; + .push(function (evt) { + var state_dict = {}, + data = JSON.parse(evt.target.responseText); state_dict['blob_state_' + blob_page] = 'OK'; - state_dict['blob_uuid_' + blob_page] = response.uuid; + state_dict['blob_uuid_' + blob_page] = data.uuid; return gadget.changeState(state_dict); }, function () { - // XXX TODO: Handle error case var state_dict = {}; state_dict['blob_state_' + blob_page] = 'error'; return gadget.changeState(state_dict); @@ -607,18 +607,29 @@ // Used when submitting the form ////////////////////////////////////////////////// .declareMethod('getContent', function () { - var gadget = this, - result = {}; - // XXX TODO: check all blob, and only return the UUID for the one in stored state - result.data_json = JSON.stringify({ - active_process: gadget.state.active_process, - image_list: gadget.state.image_list - }); - result[gadget.state.key] = JSON.stringify({ - input_value: 'XXX', - preferred_cropped_canvas_data: gadget.state.preferred_cropped_canvas_data - }); - throw new Error('not implemented getContent'); + var key, + uuid_key, + result, + gadget = this, + image_list = []; + + for (key in gadget.state) { + if (gadget.state.hasOwnProperty(key)) { + if (key.indexOf("blob_state_") !== -1 && + gadget.state[key] == "OK") { + uuid_key = "blob_uuid_" + key.replace("blob_state_", ""); + image_list.push(gadget.state[uuid_key]); + } + } + } + result = { + data_json: JSON.stringify({ + active_process: gadget.state.active_process, + image_list: image_list, + preferred_cropped_canvas_data: gadget.state.preferred_cropped_canvas_data + }) + }; + return result; }, {mutex: 'changestate'}) .declareMethod('checkValidity', function () { @@ -628,7 +639,7 @@ for (key in gadget.state) { if (gadget.state.hasOwnProperty(key)) { if (key.indexOf("blob_state_") !== -1 && - !gadget.state[key].match("deleted|stored")) { + !gadget.state[key].match("deleted|OK")) { return false; } if (key.indexOf("blob_url_") !== -1) { diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index e63633b2d88..fbff41b3da7 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.40903.42421.64529 + 981.42511.4469.1024 state @@ -262,7 +262,7 @@ - 1580845018.98 + 1580940947.83 UTC diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.py index 9bf0f212ec7..a7ccbddf6f1 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.py @@ -1,6 +1,7 @@ """ Proxy role as Manager is required here to access getResultList """ +from base64 import decodestring if REQUEST: return RuntimeError("You cannot run this script in the url") @@ -8,9 +9,10 @@ image_module = context.getPortalObject().image_module pdf_data_list = [] for result in active_process.getResultList(): - pdf_data_list.append( - image_module.newContent(data=result.detail, - portal_type="Image", - temp_object=True).convert(format="pdf")[1]) + if result.reference in image_list: + pdf_data_list.append( + image_module.newContent(data=decodestring(result.detail), + portal_type="Image", + temp_object=False).convert(format="pdf")[1]) return pdf_data_list diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.xml index 2b1fd5a7338..16b453294e4 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.xml @@ -50,7 +50,7 @@ _params - active_process, REQUEST=None + active_process, image_list, REQUEST=None _proxy_roles diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCamera.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCamera.py index 32cdbc78161..0537dc94132 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCamera.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCamera.py @@ -5,9 +5,9 @@ class StringIOWithFileName(StringIO): kw.get("title") or DateTime().strftime('%d-%m-%Y_%Hh%M')) portal = context.getPortalObject() -active_process = portal.restrictedTraverse(active_process_url) +active_process = portal.restrictedTraverse(str(active_process_url)) -pdf_data_list = context.Base_getTempImageList(active_process) +pdf_data_list = context.Base_getTempImageList(active_process, image_list) pdf_data = context.ERP5Site_mergePDFList(pdf_data_list=pdf_data_list) file_object = StringIOWithFileName(pdf_data) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCamera.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCamera.xml index 0c0905377cf..994d863fcf0 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCamera.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCamera.xml @@ -50,7 +50,7 @@ _params - publication_state, active_process_url=None, **kw + publication_state, active_process_url, image_list, **kw id diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.py index b01c490bc49..114c285b24d 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.py @@ -1,11 +1,19 @@ +import json + +data_dict = json.loads(data_json) portal = context.getPortalObject() translateString = portal.Base_translateString +active_process_url = data_dict.pop("active_process") +image_list = data_dict.pop("image_list") + # Avoid to pass huge images to the activity kw.pop("your_document_scanner_gadget", None) context.activate().Base_uploadDocumentFromCamera( + publication_state=publication_state, active_process_url=active_process_url, + image_list=image_list, **kw) return context.Base_redirect('view', diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.xml index 79f84fcd9d9..5feee9b5718 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_uploadDocumentFromCameraByActivity.xml @@ -50,7 +50,7 @@ _params - active_process_url=None, **kw + data_json, publication_state, **kw id -- 2.30.9 From 3c233d5afd4eddf1e529586255fc3dbfb809c19b Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Wed, 5 Feb 2020 22:25:20 +0000 Subject: [PATCH 29/42] erp5_document_scanner: display a nice plus image With the size close to the thumbnail, to replace the `new` button text --- .../web_page_module/rjs_gadget_document_scanner_css.css | 6 +++++- .../web_page_module/rjs_gadget_document_scanner_css.xml | 4 ++-- .../web_page_module/rjs_gadget_document_scanner_js.js | 3 +-- .../web_page_module/rjs_gadget_document_scanner_js.xml | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css index 687abf6175f..4ec4d6f3318 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.css @@ -67,9 +67,13 @@ div[data-gadget-url$="gadget_document_scanner.html"] > .camera > .thumbnail-list } div[data-gadget-url$="gadget_document_scanner.html"] .show-img { - width: 35%; + width: 21%; } div[data-gadget-url$="gadget_document_scanner.html"] .upload-error { border: 3px solid red; +} + +div[data-gadget-url$="gadget_document_scanner.html"] .new-btn { + font-size: 26pt; } \ No newline at end of file diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml index 69a3fc2674d..1770a706e29 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml @@ -244,7 +244,7 @@ serial - 981.39650.41596.57241 + 981.42527.16024.21179 state @@ -262,7 +262,7 @@ - 1580843720.26 + 1580941432.56 UTC diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index d200ec9b0db..d8ee2a237b9 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -185,10 +185,9 @@ return gadget.getTranslationList(["Page", "New Page"]) .push(function (result_list) { thumbnail_dom_list.push(domsugar('button', {type: 'button', - text: result_list[1], // Do not allow to show again the current image disabled: (len === gadget.state.page - 1), - 'class': 'new-btn' + 'class': 'new-btn ui-btn-icon-left ui-icon-plus' })); for (i = 0; i < len; i += 1) { diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index fbff41b3da7..a4b6783e80c 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.42511.4469.1024 + 981.42519.52255.48861 state @@ -262,7 +262,7 @@ - 1580940947.83 + 1580941317.11 UTC -- 2.30.9 From 4307cf0045913242cf4b90b31e68957556e65eac Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Thu, 6 Feb 2020 22:20:03 +0000 Subject: [PATCH 30/42] erp5_document_scanner: Revert code because this was changed only to debug --- .../portal_skins/erp5_document_scanner/Base_getTempImageList.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.py index a7ccbddf6f1..f3ec6775067 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getTempImageList.py @@ -13,6 +13,6 @@ for result in active_process.getResultList(): pdf_data_list.append( image_module.newContent(data=decodestring(result.detail), portal_type="Image", - temp_object=False).convert(format="pdf")[1]) + temp_object=True).convert(format="pdf")[1]) return pdf_data_list -- 2.30.9 From 1f0581bb55b4f42559211a33b4c019b8e8c9640d Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Thu, 6 Feb 2020 22:25:27 +0000 Subject: [PATCH 31/42] erp5_document_scanner: Add spinner in button to show that we are saving it in background --- .../web_page_module/rjs_gadget_document_scanner_js.js | 7 ++++++- .../web_page_module/rjs_gadget_document_scanner_js.xml | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index d8ee2a237b9..80072e97f47 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -178,6 +178,7 @@ function buildPreviousThumbnailDom(gadget) { var i, img_class, + btn_class = "", len = gadget.state.page_count, thumbnail_dom_list = []; @@ -198,8 +199,13 @@ } else { img_class = "show-img"; } + + if (gadget.state['blob_state_' + i] === "saving") { + btn_class = "ui-btn-icon-left ui-icon-spinner"; + } thumbnail_dom_list.push(domsugar('button', { type: "button", + "class": btn_class, // Do not allow to show again the current image // or do not allow to show saving image (to simplify button management) disabled: (i === gadget.state.page) || (gadget.state['blob_state_' + i] === 'saving') @@ -477,7 +483,6 @@ // Only refresh the thumbnail list // if display_step is not modified - // XXX TODO use a more precise selector return buildPreviousThumbnailDom(gadget) .push(function (result) { thumbnail_container = gadget.element.querySelector('.thumbnail-list'); diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml index a4b6783e80c..16fbd1337de 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.xml @@ -244,7 +244,7 @@ serial - 981.42519.52255.48861 + 981.43882.8130.35481 state @@ -262,7 +262,7 @@ - 1580941317.11 + 1581025205.74 UTC -- 2.30.9 From 73eca3b498fd5fc6bef2e2681eb330076823b7b7 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Thu, 6 Feb 2020 22:26:46 +0000 Subject: [PATCH 32/42] erp5_document_scanner_ui_test: Fix test to cover latest changes --- .../testScanDocument.zpt | 52 ++++++------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt b/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt index bf7ad3d3da0..4a8d87d8cf4 100644 --- a/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt +++ b/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt @@ -68,11 +68,7 @@ field_your_title Test Scan Document - + type field_your_language @@ -136,38 +132,31 @@ //button[@class="confirm-btn ui-btn-icon-left ui-icon-check"] - - - - - + + waitForElementPresent + //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] + + + + waitForElementPresent + //button[@class="ui-btn-icon-left ui-icon-spinner"] + + - - storeValue - //input[@id="field_your_active_process_url"] - active_process_url + waitForElementNotPresent + //button[@class="ui-btn-icon-left ui-icon-spinner"] + + - - open - ${base_url}/erp5/Base_assertActiveProcessHasOneImage?active_process_url=${active_process_url} - - - - assertTextPresent - OK - - - @@ -229,17 +218,6 @@ - - \ No newline at end of file -- 2.30.9 From d3571fc7eb3c1a352616bbca9e3e96c53f9959fb Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Sat, 8 Feb 2020 00:13:23 +0000 Subject: [PATCH 33/42] erp5_document_scanner_ui_test: Improve test to cover all buttons --- .../testScanDocument.zpt | 108 +++++++++++++++++- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt b/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt index 4a8d87d8cf4..45a597866e6 100644 --- a/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt +++ b/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocument.zpt @@ -91,7 +91,7 @@
- + waitForElementPresent //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] @@ -102,11 +102,17 @@ //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] + + waitForElementPresent + //div[@class="cropper-wrap-box"] + + waitForElementPresent //button[@class="reset-btn ui-btn-icon-left ui-icon-times"] + click //button[@class="reset-btn ui-btn-icon-left ui-icon-times"] @@ -117,6 +123,7 @@ //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] + click //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] @@ -127,6 +134,7 @@ //button[@class="confirm-btn ui-btn-icon-left ui-icon-check"] + click //button[@class="confirm-btn ui-btn-icon-left ui-icon-check"] @@ -137,18 +145,110 @@ //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] + waitForElementPresent //button[@class="ui-btn-icon-left ui-icon-spinner"]
+ + + waitForElementNotPresent + //button[@class="ui-btn-icon-left ui-icon-spinner"] + + + + assertElementPresent + //button[@class="new-btn ui-btn-icon-left ui-icon-plus" and @disabled] + + + + + assertElementPresent + //img[@data-page="0" and @class="show-img"] + + + + + assertElementPresent + //img[@data-page="1" and @class="show-img"] + + + + + assertElementNotPresent + //img[@data-page="2" and @class="show-img"] + + + + + click + //img[@data-page="0" and @class="show-img"] + + + + + waitForElementPresent + //button[@class="delete-btn ui-btn-icon-left ui-icon-times"] + + + + assertElementNotPresent + //button[@class="new-btn ui-btn-icon-left ui-icon-plus" and @disabled] + + + + click + //button[@class="new-btn ui-btn-icon-left ui-icon-plus"] + + + + waitForElementPresent + //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] + + + + click + //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] + + + + waitForElementPresent + //div[@class="cropper-wrap-box"] + + + + waitForElementPresent + //button[@class="confirm-btn ui-btn-icon-left ui-icon-check"] + + + + + click + //button[@class="confirm-btn ui-btn-icon-left ui-icon-check"] + + + + waitForElementPresent + //button[@class="take-picture-btn ui-btn-icon-left ui-icon-circle"] + + + + waitForElementPresent + //button[@class="ui-btn-icon-left ui-icon-spinner"] + + + + assertElementPresent + //img[@data-page="2" and @class="show-img"] + + waitForElementNotPresent //button[@class="ui-btn-icon-left ui-icon-spinner"] - + Reference + + \ No newline at end of file diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py index 685dafed6cb..b0b296e8351 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py @@ -14,6 +14,7 @@ if active_process_url: active_process = portal.restrictedTraverse(active_process_url) else: active_process = portal.portal_activities.newActiveProcess() + active_process.setReference("document_scanner_js") if generate_new_uid: id_group = ('document_scanner_js', active_process.getUid()) diff --git a/bt5/erp5_document_scanner/bt/template_portal_type_property_sheet_list b/bt5/erp5_document_scanner/bt/template_portal_type_property_sheet_list new file mode 100644 index 00000000000..daea8ebdd4c --- /dev/null +++ b/bt5/erp5_document_scanner/bt/template_portal_type_property_sheet_list @@ -0,0 +1 @@ +Active Process | Reference \ No newline at end of file -- 2.30.9 From 4b1458a534f2f6b5bea4aee53d9c9788d8a85fbd Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 10 Feb 2020 17:32:59 +0000 Subject: [PATCH 35/42] erp5_document_scanner: add an alarm which will delete outdated (>5 days old) active_process created by the dialog. --- .../remove_outdated_active_process.xml | 95 +++++++++++++++++++ ...iveProcess_removeItselfFromActivityTool.py | 5 + ...eProcess_removeItselfFromActivityTool.xml} | 12 +-- .../Alarm_removeOutdatedActiveProcess.py | 9 ++ .../Alarm_removeOutdatedActiveProcess.xml | 62 ++++++++++++ ...ase_removeActiveProcessFromActivityTool.py | 2 - .../bt/template_path_list | 1 + 7 files changed, 174 insertions(+), 12 deletions(-) create mode 100644 bt5/erp5_document_scanner/PathTemplateItem/portal_alarms/remove_outdated_active_process.xml create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/ActiveProcess_removeItselfFromActivityTool.py rename bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/{Base_removeActiveProcessFromActivityTool.xml => ActiveProcess_removeItselfFromActivityTool.xml} (85%) create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.xml delete mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_removeActiveProcessFromActivityTool.py diff --git a/bt5/erp5_document_scanner/PathTemplateItem/portal_alarms/remove_outdated_active_process.xml b/bt5/erp5_document_scanner/PathTemplateItem/portal_alarms/remove_outdated_active_process.xml new file mode 100644 index 00000000000..b09b324212d --- /dev/null +++ b/bt5/erp5_document_scanner/PathTemplateItem/portal_alarms/remove_outdated_active_process.xml @@ -0,0 +1,95 @@ + + + + + + + + + + active_sense_method_id + Alarm_removeOutdatedActiveProcess + + + automatic_solve + 0 + + + description + + + + + + enabled + 1 + + + id + remove_outdated_active_process + + + periodicity_day_frequency + 1 + + + periodicity_hour + + + + + + periodicity_minute + + + + + + periodicity_month + + + + + + periodicity_month_day + + + + + + periodicity_start_date + + + + + + + + + + + 1167609600.0 + GMT + + + + + + + periodicity_week + + + + + + portal_type + Alarm + + + title + Remove Outdated Active Process + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/ActiveProcess_removeItselfFromActivityTool.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/ActiveProcess_removeItselfFromActivityTool.py new file mode 100644 index 00000000000..b2a9ab8030a --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/ActiveProcess_removeItselfFromActivityTool.py @@ -0,0 +1,5 @@ +assert context.getPortalType() == "Active Process", "It must be an Active Process" +assert context.getReference() == "document_scanner_js", "Unexpected reference" + +context.getPortalObject().portal_activities.manage_delObjects( + ids=[context.getId(),]) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_removeActiveProcessFromActivityTool.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/ActiveProcess_removeItselfFromActivityTool.xml similarity index 85% rename from bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_removeActiveProcessFromActivityTool.xml rename to bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/ActiveProcess_removeItselfFromActivityTool.xml index 7d180b28ae5..f94ae5aacd4 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_removeActiveProcessFromActivityTool.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/ActiveProcess_removeItselfFromActivityTool.xml @@ -50,19 +50,11 @@
_params - active_process - - - _proxy_roles - - - Manager - - + id - Base_removeActiveProcessFromActivityTool + ActiveProcess_removeItselfFromActivityTool
diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py new file mode 100644 index 00000000000..2962b1147ca --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py @@ -0,0 +1,9 @@ +portal = context.getPortalObject() + +portal.portal_catalog.searchAndActivate( + portal_type=["Active Process",], + method_id='ActiveProcess_removeItselfFromActivityTool', + reference="document_scanner_js", + # Active Process don't have creation date set + indexation_timestamp="< %s" % (DateTime()-5).earliestTime(), +) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.xml new file mode 100644 index 00000000000..285f3208692 --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.xml @@ -0,0 +1,62 @@ + + + + + + + + + + Script_magic + 3 + + + _bind_names + + + + + + + + + + _asgns + + + + name_container + container + + + name_context + context + + + name_m_self + script + + + name_subpath + traverse_subpath + + + + + + + + + + + _params + + + + id + Alarm_removeOutdatedActiveProcess + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_removeActiveProcessFromActivityTool.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_removeActiveProcessFromActivityTool.py deleted file mode 100644 index 5ddd3718c0c..00000000000 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_removeActiveProcessFromActivityTool.py +++ /dev/null @@ -1,2 +0,0 @@ -context.getPortalObject().portal_activities.manage_delObjects( - ids=[active_process.getId(),]) diff --git a/bt5/erp5_document_scanner/bt/template_path_list b/bt5/erp5_document_scanner/bt/template_path_list index 87e5720ada3..a084abccffe 100644 --- a/bt5/erp5_document_scanner/bt/template_path_list +++ b/bt5/erp5_document_scanner/bt/template_path_list @@ -1,3 +1,4 @@ +portal_alarms/remove_outdated_active_process web_page_module/rjs_gadget_document_scanner_css web_page_module/rjs_gadget_document_scanner_html web_page_module/rjs_gadget_document_scanner_js \ No newline at end of file -- 2.30.9 From ac4dc171198d82d13152101ae81e03a16111b729 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 10 Feb 2020 17:39:01 +0000 Subject: [PATCH 36/42] fixup! erp5_document_scanner: add an alarm which will delete outdated (>5 days old) active_process created by the dialog. --- .../erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py index 2962b1147ca..5dc52764f64 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py @@ -5,5 +5,5 @@ portal.portal_catalog.searchAndActivate( method_id='ActiveProcess_removeItselfFromActivityTool', reference="document_scanner_js", # Active Process don't have creation date set - indexation_timestamp="< %s" % (DateTime()-5).earliestTime(), + indexation_timestamp="< %s" % (DateTime()-4).earliestTime(), ) -- 2.30.9 From 9960bba743e372645fb86fa3c0c6f6cb2d380b80 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 10 Feb 2020 22:21:06 +0000 Subject: [PATCH 37/42] erp5_document_scanner_ui_test: Test if outdated active process are removed correclty --- .../test.erp5.testDocumentScanner.py | 78 +++++++++++ .../test.erp5.testDocumentScanner.xml | 127 ++++++++++++++++++ .../bt/template_test_id_list | 3 +- 3 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 bt5/erp5_document_scanner_ui_test/TestTemplateItem/portal_components/test.erp5.testDocumentScanner.py create mode 100644 bt5/erp5_document_scanner_ui_test/TestTemplateItem/portal_components/test.erp5.testDocumentScanner.xml diff --git a/bt5/erp5_document_scanner_ui_test/TestTemplateItem/portal_components/test.erp5.testDocumentScanner.py b/bt5/erp5_document_scanner_ui_test/TestTemplateItem/portal_components/test.erp5.testDocumentScanner.py new file mode 100644 index 00000000000..3a2acfa0826 --- /dev/null +++ b/bt5/erp5_document_scanner_ui_test/TestTemplateItem/portal_components/test.erp5.testDocumentScanner.py @@ -0,0 +1,78 @@ +############################################################################## +# +# Copyright (c) 2002-2020 Nexedi SA and Contributors. All Rights Reserved. +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsibility of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# guarantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +############################################################################## + +import json +from DateTime import DateTime +from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase + +class DocumentScanner(ERP5TypeTestCase): + """ + Document Scanner Test Case + """ + + def getTitle(self): + return "Test Document Scanner" + + def afterSetUp(self): + """ + This is ran before anything, used to set the environment + """ + + id_list = [o.getId() for o in self.portal.portal_catalog( + reference="document_scanner_js", + portal_type="Active Process")] + if id_list: + self.portal.portal_activities.manage_delObjects(ids=id_list) + self.tic() + + def test_remove_outdated_active_process(self): + data_dict = json.loads( + self.portal.Base_getDefaultBackendDataAsJSON()) + self.assertEqual(data_dict["image_list"], []) + active_process_url = data_dict["active_process"] + active_process = self.portal.restrictedTraverse(str(active_process_url)) + self.assertEqual(active_process.getResultList(), []) + data_png = ("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA" + "AEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgm" + "WQ0AAAAASUVORK5CYII=") + response = self.portal.Base_storeNewImageCropped(data_png, + active_process.getRelativeUrl()) + first_uuid = json.loads(response)["uuid"] + self.assertIn(first_uuid, [r.reference for r in active_process.getResultList()]) + response = self.portal.Base_storeNewImageCropped(data_png, + active_process.getRelativeUrl()) + second_uuid = json.loads(response)["uuid"] + self.assertIn(second_uuid, [r.reference for r in active_process.getResultList()]) + self.assertIn(first_uuid, [r.reference for r in active_process.getResultList()]) + self.tic() + req = self.portal.erp5_sql_connection.manage_test + req("update catalog set modification_date='{date}' where uid={uid}".format( + date=(DateTime()-6).toZone('UTC'), + uid=active_process.getUid())) + self.portal.portal_alarms.remove_outdated_active_process.activeSense() + self.tic() + self.assertNotIn(active_process.getId(), list(self.portal.portal_activities.objectIds())) \ No newline at end of file diff --git a/bt5/erp5_document_scanner_ui_test/TestTemplateItem/portal_components/test.erp5.testDocumentScanner.xml b/bt5/erp5_document_scanner_ui_test/TestTemplateItem/portal_components/test.erp5.testDocumentScanner.xml new file mode 100644 index 00000000000..6f737ff7f15 --- /dev/null +++ b/bt5/erp5_document_scanner_ui_test/TestTemplateItem/portal_components/test.erp5.testDocumentScanner.xml @@ -0,0 +1,127 @@ + + + + + + + + + + _recorded_property_dict + + AAAAAAAAAAI= + + + + default_reference + testDocumentScanner + + + description + + + + + + id + test.erp5.testDocumentScanner + + + portal_type + Test Component + + + sid + + + + + + text_content_error_message + + + + + + text_content_warning_message + + + + + + version + erp5 + + + workflow_history + + AAAAAAAAAAM= + + + + + + + + + + + + + data + + + + + + + + + + + + + + + data + + + + component_validation_workflow + + AAAAAAAAAAQ= + + + + + + + + + + + + + + + + _log + + + + + action + validate + + + validation_state + validated + + + + + + + + + diff --git a/bt5/erp5_document_scanner_ui_test/bt/template_test_id_list b/bt5/erp5_document_scanner_ui_test/bt/template_test_id_list index cd9f2ccdeba..ff218781410 100644 --- a/bt5/erp5_document_scanner_ui_test/bt/template_test_id_list +++ b/bt5/erp5_document_scanner_ui_test/bt/template_test_id_list @@ -1 +1,2 @@ -test.erp5.testRenderJSDocumentScanner \ No newline at end of file +test.erp5.testRenderJSDocumentScanner +test.erp5.testDocumentScanner \ No newline at end of file -- 2.30.9 From bdde7617450e74f50851f4115d32c0e3cf06999b Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Mon, 10 Feb 2020 22:27:43 +0000 Subject: [PATCH 38/42] erp5_document_scanner: Avoid reference hardcoded in many places --- ...iveProcess_removeItselfFromActivityTool.py | 2 +- .../Alarm_removeOutdatedActiveProcess.py | 4 +- .../Base_getDefaultBackendDataAsJSON.py | 3 +- ...Base_getDocumentScannerDefaultReference.py | 1 + ...ase_getDocumentScannerDefaultReference.xml | 62 +++++++++++++++++++ .../Base_postDataToActiveResult.py | 7 ++- 6 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDocumentScannerDefaultReference.py create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDocumentScannerDefaultReference.xml diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/ActiveProcess_removeItselfFromActivityTool.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/ActiveProcess_removeItselfFromActivityTool.py index b2a9ab8030a..fe9f5217da2 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/ActiveProcess_removeItselfFromActivityTool.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/ActiveProcess_removeItselfFromActivityTool.py @@ -1,5 +1,5 @@ assert context.getPortalType() == "Active Process", "It must be an Active Process" -assert context.getReference() == "document_scanner_js", "Unexpected reference" +assert context.getReference() == context.Base_getDocumentScannerDefaultReference(), "Unexpected reference" context.getPortalObject().portal_activities.manage_delObjects( ids=[context.getId(),]) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py index 5dc52764f64..62cd3545bbe 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Alarm_removeOutdatedActiveProcess.py @@ -3,7 +3,7 @@ portal = context.getPortalObject() portal.portal_catalog.searchAndActivate( portal_type=["Active Process",], method_id='ActiveProcess_removeItselfFromActivityTool', - reference="document_scanner_js", + reference=context.Base_getDocumentScannerDefaultReference(), # Active Process don't have creation date set - indexation_timestamp="< %s" % (DateTime()-4).earliestTime(), + modification_date="< %s" % (DateTime()-4).earliestTime(), ) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py index 6f8e9065fbf..c9b44ae8d9f 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDefaultBackendDataAsJSON.py @@ -1,6 +1,7 @@ import json -active_process = context.getPortalObject().portal_activities.newActiveProcess() +active_process = context.getPortalObject().portal_activities.newActiveProcess( + reference=context.Base_getDocumentScannerDefaultReference()) return json.dumps({ "active_process": active_process.getRelativeUrl(), diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDocumentScannerDefaultReference.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDocumentScannerDefaultReference.py new file mode 100644 index 00000000000..3c9deb1dcf6 --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDocumentScannerDefaultReference.py @@ -0,0 +1 @@ +return "document_scanner_js" diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDocumentScannerDefaultReference.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDocumentScannerDefaultReference.xml new file mode 100644 index 00000000000..ce94818e61d --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getDocumentScannerDefaultReference.xml @@ -0,0 +1,62 @@ + + + + + + + + + + Script_magic + 3 + + + _bind_names + + + + + + + + + + _asgns + + + + name_container + container + + + name_context + context + + + name_m_self + script + + + name_subpath + traverse_subpath + + + + + + + + + + + _params + + + + id + Base_getDocumentScannerDefaultReference + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py index b0b296e8351..ad5835bb80f 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_postDataToActiveResult.py @@ -10,14 +10,15 @@ portal = context.getPortalObject() if REQUEST: return RuntimeError("You cannot run this script in the url") +reference = context.Base_getDocumentScannerDefaultReference() if active_process_url: active_process = portal.restrictedTraverse(active_process_url) else: - active_process = portal.portal_activities.newActiveProcess() - active_process.setReference("document_scanner_js") + active_process = portal.portal_activities.newActiveProcess( + reference=reference) if generate_new_uid: - id_group = ('document_scanner_js', active_process.getUid()) + id_group = (reference, active_process.getUid()) new_uid = portal.portal_ids.generateNewId(id_group=id_group, default=0) else: new_uid = None -- 2.30.9 From 3f12390712af7705116f918542fbac331ad1d372 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Fri, 29 Nov 2019 09:51:29 +0000 Subject: [PATCH 39/42] [erp5_document_scanner] Extend gadget to allow image manipulation with Caman JS With this, from user preference we can define greyscale, brightness and contrast --- .../document_scanner_preference.xml | 81 ++ .../rjs_gadget_document_scanner_css.xml | 2 +- .../rjs_gadget_document_scanner_html.html | 3 +- .../rjs_gadget_document_scanner_js.js | 860 ++++++------------ .../DocumentScannerPreference.xml | 66 ++ ...d_image_conversion_brightness_property.xml | 59 ++ ..._image_conversion_compression_property.xml | 59 ++ ...red_image_conversion_contrast_property.xml | 59 ++ ...e_conversion_enable_greyscale_property.xml | 59 ++ ...etPreferredCropperSettingsFromSelection.py | 1 - ...getPreferredImageSettingsFromPreference.py | 15 + ...etPreferredImageSettingsFromPreference.xml | 62 ++ ..._storeDocumentFromCameraInActiveProcess.py | 2 +- .../your_document_scanner_gadget.xml | 2 +- ...Preference_viewDocumentScannerSettting.xml | 146 +++ ..._preferred_image_conversion_brightness.xml | 96 ++ ...preferred_image_conversion_compression.xml | 96 ++ ...my_preferred_image_conversion_contrast.xml | 96 ++ ...rred_image_conversion_enable_greyscale.xml | 96 ++ .../caman.full.min.js.js | 251 +++++ .../caman.full.min.js.xml | 32 + .../bt/template_action_path_list | 1 + .../bt/template_property_sheet_id_list | 1 + 23 files changed, 1546 insertions(+), 599 deletions(-) create mode 100644 bt5/erp5_document_scanner/ActionTemplateItem/portal_types/Preference/document_scanner_preference.xml create mode 100644 bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference.xml create mode 100644 bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_brightness_property.xml create mode 100644 bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_compression_property.xml create mode 100644 bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_contrast_property.xml create mode 100644 bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_enable_greyscale_property.xml create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.py create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.xml create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting.xml create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_brightness.xml create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_compression.xml create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_contrast.xml create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_enable_greyscale.xml create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/caman.full.min.js.js create mode 100644 bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/caman.full.min.js.xml create mode 100644 bt5/erp5_document_scanner/bt/template_property_sheet_id_list diff --git a/bt5/erp5_document_scanner/ActionTemplateItem/portal_types/Preference/document_scanner_preference.xml b/bt5/erp5_document_scanner/ActionTemplateItem/portal_types/Preference/document_scanner_preference.xml new file mode 100644 index 00000000000..7a0c764808b --- /dev/null +++ b/bt5/erp5_document_scanner/ActionTemplateItem/portal_types/Preference/document_scanner_preference.xml @@ -0,0 +1,81 @@ + + + + + + + + + + action + + AAAAAAAAAAI= + + + + categories + + + action_type/object_view + + + + + category + object_view + + + condition + + + + description + + + + + + icon + + + + id + document_scanner_preference + + + permissions + + + View + + + + + priority + 4.0 + + + title + Document Scanner + + + visible + 1 + + + + + + + + + + + + text + string:${object_url}/Preference_viewDocumentScannerSettting + + + + + diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml index 1770a706e29..bb3b92ec182 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_css.xml @@ -262,7 +262,7 @@ - 1580941432.56 + 1575027025.78 UTC diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html index 03f0fda25c4..130264f3545 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_html.html @@ -18,10 +18,11 @@ + Gadget Document Scanner
- \ No newline at end of file + diff --git a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js index 80072e97f47..b8f0ccc631e 100644 --- a/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js +++ b/bt5/erp5_document_scanner/PathTemplateItem/web_page_module/rjs_gadget_document_scanner_js.js @@ -1,664 +1,336 @@ -/*jslint indent: 2, unparam: true */ -/*global rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap, FormData*/ -(function (rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap, FormData) { +/*jslint indent: 2 */ +/*global rJS, RSVP, window, navigator, Cropper, Promise, JSON, jIO, Caman*/ +(function (rJS, RSVP, window, navigator, Cropper, Promise, JSON, jIO, Caman) { "use strict"; - ////////////////////////////////////////////////// - // Browser API to promise - ////////////////////////////////////////////////// - function promiseUserMedia(device_id) { - return navigator.mediaDevices.getUserMedia({ - video: { - deviceId: { - exact: device_id - } - }, - audio: false - }); - } - - function handleUserMedia(device_id, callback) { - // Do not modify this function! - // There is no need to add the gadget logic inside - var stream; - - function canceller() { - if (stream !== undefined) { - // Stop the streams - stream.getTracks().forEach(function (track) { - track.stop(); + function startCropper(gadget, img) { + var queue, + settings = gadget.props.preferred_image_settings_data, + root = gadget.element, + canvas = document.createElement("canvas"); + + queue = new RSVP.Queue(); + if (settings.brightness || settings.contrast || settings.enable_greyscale || settings.compression) { + queue.push(function () { + return new Promise(function (resolve) { + Caman(canvas, img.src, function () { + if (settings.brightness !== 0) { + this.brightness(settings.brightness); + } + if (settings.contrast !== 0) { + this.contrast(settings.contrast); + } + if (settings.enable_greyscale) { + this.greyscale(); + } + this.render(function () { + resolve(canvas.toDataURL("image/jpeg", settings.compression)); + }); + }); }); - } + }); } - function waitForStream(resolve, reject) { - new RSVP.Queue() - .push(function () { - return promiseUserMedia(device_id); - }) - .push(function (result) { - stream = result; - return callback(stream); - }) - .push(undefined, function (error) { - if (!(error instanceof RSVP.CancellationError)) { - canceller(); - reject(error); - } - }); - } - return new RSVP.Promise(waitForStream, canceller); - } - - function handleCropper(element, data, callback) { - var cropper; - - function canceller() { - cropper.destroy(); - } + queue.push(function (data_str) { + if (data_str) { + img.setAttribute("src", data_str); + } - // creating Cropper is asynchronous - return new RSVP.Promise(function (resolve, reject) { - cropper = new Cropper(element, { - data: data, - ready: function () { - return new RSVP.Queue() - .push(function () { - return callback(cropper); - }) - .push(undefined, function (error) { - if (!(error instanceof RSVP.CancellationError)) { - canceller(); - reject(error); - } - }); - } + root.querySelector(".camera-output").style.display = ""; + if (gadget.props.cropper) { + gadget.props.cropper.destroy(); + } + // creating Cropper is asynchronous + return new RSVP.Promise(function (resolve) { + gadget.props.cropper = new Cropper(root.querySelector('.photo'), { + data: gadget.props.preferred_cropped_canvas_data, + ready: resolve + }); }); - }, canceller); + }); + return queue; } - function handleAsyncStore(gadget, blob_page) { - var data = new FormData(); - data.append("input_value", - gadget.state['blob_url_' + blob_page].split(';')[1].split(',')[1]); - data.append("active_process_url", gadget.state.active_process); + function takePicture(gadget) { + var el = gadget.element, + image_capture = gadget.props.image_capture; return new RSVP.Queue() .push(function () { - return jIO.util.ajax({ - "type": "POST", - "url": gadget.state.store_new_image_cropped_method, - "data": data, - "xhrFields": { - withCredentials: true - } + return image_capture.takePhoto({ + imageWidth: gadget.props.image_width, + imageHeight: gadget.props.image_height }); }) - .push(function (evt) { - var state_dict = {}, - data = JSON.parse(evt.target.responseText); - state_dict['blob_state_' + blob_page] = 'OK'; - state_dict['blob_uuid_' + blob_page] = data.uuid; - return gadget.changeState(state_dict); - }, function () { - var state_dict = {}; - state_dict['blob_state_' + blob_page] = 'error'; - return gadget.changeState(state_dict); - }); - } - - ////////////////////////////////////////////////// - // helper function - ////////////////////////////////////////////////// - function getVideoDeviceList() { - if (!navigator.mediaDevices) { - throw new Error("mediaDevices is not supported"); - } - - return new RSVP.Queue() - .push(function () { - return navigator.mediaDevices.enumerateDevices(); + .push(function (blob) { + return jIO.util.readBlobAsDataURL(blob); }) - .push(function (info_list) { - var j, - device, - len = info_list.length, - device_list = []; - - for (j = len - 1; j >= 0; j -= 1) { - // trick to select back camera in mobile - device = info_list[j]; - if (device.kind === 'videoinput') { - device_list.push(device); - } - } - return device_list; + .push(function (result) { + var photoInput = el.querySelector(".photoInput"), + photo = el.querySelector("img"), + data_str = result.target.result; + + photo.setAttribute("src", data_str); + photoInput.setAttribute("value", data_str.split(",")[1]); + return startCropper(gadget, photo); }); } - function selectMediaDevice(current_device_id, force_new_device) { - return getVideoDeviceList() - .push(function (info_list) { - var j, - device, - len = info_list.length; - - for (j = len - 1; j >= 0; j -= 1) { - // trick to select back camera in mobile - device = info_list[j]; - if (device.kind === 'videoinput') { - if ((!current_device_id) || - (force_new_device && (device.deviceId !== current_device_id)) || - (!force_new_device && (device.deviceId === current_device_id))) { - return device.deviceId; - } - } - } - throw new Error("no media found"); - }); + function enableButton(root) { + [".reset-btn", ".take-picture-btn", + ".confirm-btn", ".change-camera-btn"].forEach(function (e) { + root.querySelector(e).disabled = false; + }); } - ////////////////////////////////////////////////// - // Private gadget function - ////////////////////////////////////////////////// - function addDetachedPromise(gadget, key, promise) { - // XXX TODO Handle error - if (gadget.detached_promise_dict.hasOwnProperty(key)) { - gadget.detached_promise_dict[key].cancel('Replacing key: ' + key); + function setPageOne(gadget) { + var root = gadget.element; + root.querySelector(".page-number").innerText = gadget.props.page_number; + root.querySelector(".reset-btn").style.display = "none"; + root.querySelector(".take-picture-btn").style.display = "inline-block"; + root.querySelector(".confirm-btn").style.display = "none"; + root.querySelector(".camera-input").style.display = ""; + if (gadget.props.camera_list.length > 1) { + root.querySelector(".change-camera-btn").style.display = "inline-block"; } - gadget.detached_promise_dict[key] = new RSVP.Queue() - .push(function () { - return promise; - }) - .push(undefined, function (error) { - // Crash the gadget if the detached promise raise an unexpected error - gadget.raise(error); - }); + return enableButton(root); } - function buildPreviousThumbnailDom(gadget) { - var i, - img_class, - btn_class = "", - len = gadget.state.page_count, - thumbnail_dom_list = []; - - - return gadget.getTranslationList(["Page", "New Page"]) - .push(function (result_list) { - thumbnail_dom_list.push(domsugar('button', {type: 'button', - // Do not allow to show again the current image - disabled: (len === gadget.state.page - 1), - 'class': 'new-btn ui-btn-icon-left ui-icon-plus' - })); - - for (i = 0; i < len; i += 1) { - // XXX TODO display a loader when saving - if (gadget.state['blob_state_' + i] !== 'deleted') { - if (gadget.state['blob_state_' + i] === "error") { - img_class = "show-img upload-error"; - } else { - img_class = "show-img"; - } - - if (gadget.state['blob_state_' + i] === "saving") { - btn_class = "ui-btn-icon-left ui-icon-spinner"; - } - thumbnail_dom_list.push(domsugar('button', { - type: "button", - "class": btn_class, - // Do not allow to show again the current image - // or do not allow to show saving image (to simplify button management) - disabled: (i === gadget.state.page) || (gadget.state['blob_state_' + i] === 'saving') - }, [domsugar("img", {"class": img_class, - 'data-page': i, - src: gadget.state['blob_url_' + i]})])); - } - } - return domsugar('ol', {"class": "thumbnail-list"}, thumbnail_dom_list); - }); + function setPageTwo(root) { + root.querySelector(".reset-btn").style.display = "inline-block"; + root.querySelector(".confirm-btn").style.display = "inline-block"; + root.querySelector(".take-picture-btn").style.display = "none"; + root.querySelector(".camera-input").style.display = "none"; + root.querySelector(".camera-output").style.display = ""; + root.querySelector(".change-camera-btn").style.display = "none"; } - // Display the video stream from a media source - function renderVideoCapture(gadget) { - var video; - return RSVP.Queue() - .push(function () { - var defer = RSVP.defer(); - addDetachedPromise(gadget, 'media_stream', - handleUserMedia(gadget.state.device_id, defer.resolve)); - return defer.promise; - }) - .push(function (media_stream) { - video = document.createElement('video'); - video.srcObject = media_stream; - video.autoplay = "autoplay"; - video.loop = "loop"; - video.muted = "muted"; + function disableButton(root) { + [".reset-btn", ".take-picture-btn", + ".confirm-btn", ".change-camera-btn"].forEach(function (e) { + root.querySelector(e).disabled = true; + }); + } - return RSVP.any([ - // Wait for the video to be ready - promiseEventListener(video, 'loadedmetadata', true), - promiseEventListener(video, 'canplaythrough', true), - promiseEventListener(video, 'error', true, function () { - throw new Error("Can't play the video file"); - }) - ]); - }) + function handleUserMedia(gadget, callback) { + var stream, + video = gadget.props.video; - .push(function () { - video.play(); - return RSVP.all([ - getVideoDeviceList(), - gadget.getTranslationList(["Capture", "Change Camera"]), - buildPreviousThumbnailDom(gadget) - ]); - }) - .push(function (result_list) { - var button_list = [ - domsugar('button', {type: 'button', - 'class': 'take-picture-btn ui-btn-icon-left ui-icon-circle', - text: result_list[1][0] - }) - ], - div; - // Only display the change camera if device has at least 2 cameras - if (result_list[0].length > 1) { - button_list.push( - domsugar('button', {type: 'button', - 'class': 'change-camera-btn ui-icon-refresh ui-btn-icon-left', - text: result_list[1][1] - }) - ); - } + video.autoplay = "autoplay"; - div = domsugar('div', {'class': 'camera'}, [ - domsugar('div', {'class': 'camera-header'}, [ - domsugar('h4', [ - 'Page ', - domsugar('label', {'class': 'page-number', text: gadget.state.page}) - ]) - ]), - domsugar('div', {'class': 'camera-input'}, [video]), - domsugar('div', {'class': 'edit-picture'}, button_list), - result_list[2] - ]); + function canceller() { + if (stream !== undefined) { + // Stop the streams + stream.getTracks().forEach(function (track) { + track.stop(); + }); + } + } - gadget.element.replaceChild(div, gadget.element.firstElementChild); + function waitForStream() { + return new RSVP.Queue() + .push(function () { + return navigator.mediaDevices.getUserMedia({ + video: { + deviceId: { + exact: gadget.props.device_id + } + } + }); + }) + .push(function (mediaStream) { + stream = mediaStream; + video.srcObject = mediaStream; + return callback(gadget, stream); + }) + .push(undefined, function (error) { + if (!(error instanceof RSVP.CancellationError)) { + canceller(); + } + }); + } - }); + return new RSVP.Promise(waitForStream, canceller); } - // Capture the media stream - function captureAndRenderPicture(gadget) { - var image_capture = new window.ImageCapture( - gadget.element.querySelector('video').srcObject.getVideoTracks()[0] - ), - div; + function gotStream(gadget, mediaStream) { return new RSVP.Queue() .push(function () { + var image_capture; + image_capture = new window.ImageCapture(mediaStream.getVideoTracks()[0]); + gadget.props.image_capture = image_capture; return image_capture.getPhotoCapabilities(); }) - .push(function (capabilities) { - return image_capture.takePhoto({imageWidth: capabilities.imageWidth.max}); - }) - .push(function (blob) { - gadget.detached_promise_dict.media_stream.cancel('Not needed anymore, as captured'); - return RSVP.all([ - gadget.getTranslationList(["Delete", "Save"]), - createImageBitmap(blob), - buildPreviousThumbnailDom(gadget) - ]); + .push(function (photoCapabilities) { + gadget.props.image_width = photoCapabilities.imageWidth.max; + gadget.props.image_height = photoCapabilities.imageHeight.max; + return gadget.props.video.play(); }) - .push(function (result_list) { - var bitmap = result_list[1], - canvas = domsugar('canvas', {'class': 'canvas'}), - defer = RSVP.defer(); - - // Prepare the cropper canvas - canvas.width = bitmap.width; - canvas.height = bitmap.height; - canvas.getContext('2d').drawImage(bitmap, 0, 0); - - div = domsugar('div', {'class': 'camera'}, [ - domsugar('div', {'class': 'camera-header'}, [ - domsugar('h4', [ - 'Page ', - domsugar('label', {'class': 'page-number', text: gadget.state.page}) - ]) - ]), - canvas, - domsugar('div', {'class': 'edit-picture'}, [ - domsugar('button', {type: 'button', - 'class': 'reset-btn ui-btn-icon-left ui-icon-times', - text: result_list[0][0] - }), - domsugar('button', {type: 'button', - 'class': 'confirm-btn ui-btn-icon-left ui-icon-check', - text: result_list[0][1] - }) - ]), - result_list[2] - ]); - - // XXX How to change the dom only when cropper is ready? - // For now, it needs to access dom element size - gadget.element.replaceChild(div, gadget.element.firstElementChild); - - addDetachedPromise(gadget, 'cropper', - handleCropper(canvas, - gadget.state.preferred_cropped_canvas_data, - defer.resolve)); - return defer.promise; - }) - .push(function (cropper) { - gadget.cropper = cropper; + .push(function () { + return setPageOne(gadget); }); } - function renderSubmittedPicture(gadget) { - return new RSVP.Queue() - .push(function () { - return RSVP.all([ - gadget.getTranslationList(["Delete", "Save"]), - buildPreviousThumbnailDom(gadget) - ]); - }) - .push(function (result_list) { - var button_list = [ - // XXX TODO: improve icon - domsugar('button', {type: 'button', - 'class': 'delete-btn ui-btn-icon-left ui-icon-times', - text: result_list[0][0] - }) - ], - div; - - if (gadget.state['blob_state_' + gadget.state.page] === 'error') { - button_list.push( - // XXX TODO improve icon - domsugar('button', {type: 'button', - 'class': 'retry-btn ui-btn-icon-left ui-icon-times', - text: result_list[0][1] - }) - ); - } - - div = domsugar('div', {'class': 'camera'}, [ - domsugar('div', {'class': 'camera-header'}, [ - domsugar('h4', [ - 'Page ', - domsugar('label', {'class': 'page-number', text: gadget.state.page + 1}) - ]) - ]), - domsugar('img', {src: gadget.state['blob_url_' + gadget.state.page]}), - // XXX TODO: why is the button rendering different from the other pages? - domsugar('div', {'class': 'edit-picture'}, button_list), - result_list[1] - ]); - - // XXX How to change the dom only when cropper is ready? - // For now, it needs to access dom element size - gadget.element.replaceChild(div, gadget.element.firstElementChild); - }); + function startStream(gadget) { + return handleUserMedia(gadget, gotStream); } - ////////////////////////////////////////////////// - // Gadget API - ////////////////////////////////////////////////// rJS(window) - .ready(function () { - this.detached_promise_dict = {}; - }) - .declareJob('raise', function (error) { - throw error; + .declareAcquiredMethod( + "submitDialogWithCustomDialogMethod", + "submitDialogWithCustomDialogMethod" + ) + .declareAcquiredMethod("getTranslationList", "getTranslationList") + .declareAcquiredMethod("notifySubmitted", "notifySubmitted") + .declareJob("startStream", function () { + return startStream(this); }) - .declareService(function handleDetachedPromiseDict() { - // This service is responsable to cancel all ongoing detached promises - // if the gadget is removed from the page - var gadget = this; - return new RSVP.Promise(function () {return; }, function canceller(msg) { - var key; - for (key in gadget.detached_promise_dict) { - if (gadget.detached_promise_dict.hasOwnProperty(key)) { - gadget.detached_promise_dict[key].cancel(msg); - } - } - }); - }) - - .setState({ - display_step: 'display_video', - page: 1, - page_count: 0 + .ready(function () { + this.props = { + video: this.element.querySelector(".video") + }; }) .declareMethod('render', function (options) { - // This method is called during the ERP5 form rendering - // changeState is used to ensure not resetting the gadget current display - // if not needed - var gadget = this, - default_value = JSON.parse(options.value); - return selectMediaDevice(gadget.state.device_id, false) - .push(function (device_id) { - return gadget.changeState({ - dialog_method: options.dialog_method, - store_new_image_cropped_method: options.store_new_image_cropped_method, - active_process: default_value.active_process, - image_list: default_value.image_list, - preferred_cropped_canvas_data: JSON.parse(options.preferred_cropped_canvas_data), - device_id: device_id, - key: options.key, - first_render: true - }); + var root = this.element, + camera_list = [], + gadget = this; + + return this.getTranslationList(["Webcam is not available", "Reset", "Take Picture", "Confirm", "Edit", "Change Camera"]) + .push(function (result_list) { + var i, + button_list = root.querySelectorAll("button"); + for (i = 0; i < button_list.length; i += 1) { + button_list[i].innerText = " " + result_list[i + 1]; + } + root.querySelector("video").innerText = result_list[0]; + }) + .push(function () { + gadget.props.preferred_cropped_canvas_data = gadget.props.preferred_cropped_canvas_data || JSON.parse(options.preferred_cropped_canvas_data); + gadget.props.preferred_image_settings_data = JSON.parse(options.preferred_image_settings_data); + gadget.props.dialog_method = options.dialog_method; + // Clear photo input + root.querySelector('.photoInput').value = ""; + gadget.props.page_number = parseInt(root.querySelector('input[name="page-number"]').value, 10); + root.querySelector(".camera-input").style.display = ""; + root.querySelector(".camera-output").style.display = "none"; + + if (!navigator.mediaDevices) { + throw ("mediaDevices is not supported"); + } + gadget.props.preferred_cropped_canvas_data = preferred_cropped_canvas_data; + return navigator.mediaDevices.enumerateDevices(); + }) + .push(function (info_list) { + var j, + device, + len = info_list.length; + + if (camera_list.length === 0) { + for (j = 0; j < len; j += 1) { + device = info_list[j]; + if (device.kind === 'videoinput') { + camera_list.push(device); + } + } + } + if (camera_list.length >= 1) { + // trick to select back camera in mobile + gadget.props.device_id = camera_list[camera_list.length - 1].deviceId; + } + gadget.props.camera_list = camera_list; + return gadget.startStream(); }); }) + .declareMethod('getContent', function () { + var input = this.element.querySelector('.photoInput'), + result = {}; - .onStateChange(function (modification_dict) { - var gadget = this, - display_step, - thumbnail_container; - // ALL DOM modifications must be done only in this method - // this prevent concurrency issue on DOM access - - // Only refresh the full gadget content after the first render call - // or if the display_step is modified - // or if displaying another image - if (modification_dict.first_render || modification_dict.hasOwnProperty('page')) { - display_step = gadget.state.display_step; - } else { - display_step = modification_dict.display_step; - } - if (display_step === 'display_video' || modification_dict.hasOwnProperty('device_id')) { - return renderVideoCapture(gadget); - } - if (display_step === 'crop_picture') { - return captureAndRenderPicture(gadget); - } - if (display_step === 'show_picture') { - return renderSubmittedPicture(gadget); - } - if (display_step) { - // Ease developper work by raising for not handled cases - throw new Error('Unhandled display step: ' + gadget.state.display_step); - } - - // Only refresh the thumbnail list - // if display_step is not modified - return buildPreviousThumbnailDom(gadget) - .push(function (result) { - thumbnail_container = gadget.element.querySelector('.thumbnail-list'); - thumbnail_container.parentElement.replaceChild( - result, - thumbnail_container - ); - }); + result.field_your_document_scanner_gadget = JSON.stringify({ + "input_value": input.value, + "preferred_cropped_canvas_data": this.props.preferred_cropped_canvas_data + }); + return result; }) - .onEvent("click", function (evt) { - // Only handle click on BUTTON element - if (evt.target.tagName !== 'BUTTON' && evt.target.tagName !== 'IMG') { - return; - } - - var gadget = this, - state_dict; + var e, + new_preferred_cropped_canvas_data, + gadget = this, + camera_list = this.props.camera_list, + root = this.element; - // Disable any button. It must be managed by this gadget - evt.preventDefault(); - gadget.element.querySelectorAll('button').forEach(function (elt) { - elt.disabled = true; - }); + if (evt.target.className.indexOf("change-camera-btn") !== -1) { + evt.preventDefault(); + for (e in camera_list) { + if (camera_list.hasOwnProperty(e)) { + if (camera_list[e].deviceId !== gadget.props.device_id) { + gadget.props.device_id = camera_list[e].deviceId; + break; + } + } + } + return gadget.startStream(); + } if (evt.target.className.indexOf("take-picture-btn") !== -1) { - return gadget.changeState({ - display_step: 'crop_picture' - }); + evt.preventDefault(); + return new RSVP.Queue() + .push(function () { + disableButton(root); + root.querySelector(".camera").style.maxWidth = gadget.props.video.offsetWidth + "px"; + return takePicture(gadget); + }) + .push(function () { + root.querySelector(".camera-input").style.display = "none"; + setPageTwo(root); + return enableButton(root); + }); } - if (evt.target.className.indexOf("reset-btn") !== -1) { - return gadget.changeState({ - display_step: 'display_video' - }); - } - - if (evt.target.className.indexOf("new-btn") !== -1) { - return gadget.changeState({ - display_step: 'display_video', - page: gadget.state.page_count + 1 - }); - } - - if (evt.target.className.indexOf("delete-btn") !== -1) { - state_dict = { - display_step: 'display_video', - page: gadget.state.page_count + 1 - }; - state_dict['blob_state_' + gadget.state.page] = 'deleted'; - return gadget.changeState(state_dict); + evt.preventDefault(); + root.querySelector(".camera-input").style.display = ""; + root.querySelector(".camera-output").style.display = "none"; + root.querySelector('.photoInput').value = ""; + if (gadget.props.cropper) { + gadget.props.cropper.destroy(); + } + return setPageOne(gadget); } - if (evt.target.className.indexOf("confirm-btn") !== -1) { + evt.preventDefault(); + new_preferred_cropped_canvas_data = gadget.props.cropper.getData(); + for (e in new_preferred_cropped_canvas_data) { + if (new_preferred_cropped_canvas_data.hasOwnProperty(e)) { + gadget.props.preferred_cropped_canvas_data[e] = new_preferred_cropped_canvas_data[e]; + } + } return new RSVP.Queue() .push(function () { - var canvas = gadget.cropper.getCroppedCanvas(); + var canvas = gadget.props.cropper.getCroppedCanvas(); + disableButton(gadget.element); return new Promise(function (resolve) { - canvas.toBlob(resolve, 'image/jpeg', 0.85); + canvas.toBlob(resolve, 'image/jpeg', 1); }); }) .push(function (blob) { return jIO.util.readBlobAsDataURL(blob); }) - .push(function (evt) { - state_dict = { - preferred_cropped_canvas_data: gadget.cropper.getData(), - display_step: 'display_video', - page: gadget.state.page + 1, - page_count: gadget.state.page_count + 1 - }; - // Keep image date, as user may need to display it again - state_dict['blob_url_' + gadget.state.page_count] = evt.target.result; - state_dict['blob_state_' + gadget.state.page_count] = 'saving'; - return gadget.changeState(state_dict); + .push(function (result) { + var base64data = result.target.result, + block = base64data.split(";"), + realData = block[1].split(",")[1]; + root.querySelector(".photo").src = base64data; + root.querySelector(".photoInput").value = realData; + if (gadget.props.proper) { + gadget.props.cropper.destroy(); + } }) .push(function () { - // XXX Ensure that you have the active process relative url - addDetachedPromise(gadget, 'ajax_' + (gadget.state.page_count - 1), - handleAsyncStore(gadget, gadget.state.page_count - 1)); - - gadget.detached_promise_dict.cropper.cancel('Not needed anymore, as cropped'); - }); - } - - if (evt.target.className.indexOf("retry-btn") !== -1) { - // XXX Ensure that you have the active process relative url - addDetachedPromise(gadget, 'ajax_' + (gadget.state.page), - handleAsyncStore(gadget, gadget.state.page)); - state_dict = { - display_step: 'display_video', - page: gadget.state.page_count + 1 - }; - state_dict['blob_state_' + gadget.state.page] = 'saving'; - return gadget.changeState(state_dict); - } - - if (evt.target.className.indexOf("change-camera-btn") !== -1) { - return selectMediaDevice(gadget.state.device_id, true) - .push(function (device_id) { - return gadget.changeState({ - display_step: 'display_video', - device_id: device_id - }); + return gadget.submitDialogWithCustomDialogMethod(gadget.props.dialog_method); + }) + .push(function () { + gadget.props.page_number = gadget.props.page_number + 1; + root.querySelector('input[name="page-number"]').value = gadget.props.page_number; }); } + }, false, false); - if (evt.target.className.indexOf("show-img") !== -1) { - if (gadget.detached_promise_dict.cropper) { - gadget.detached_promise_dict.cropper.cancel('Not needed anymore, as cancelled'); - } - if (gadget.detached_promise_dict.media_stream) { - gadget.detached_promise_dict.media_stream.cancel('Not needed anymore, as cancelled'); - } - - return gadget.changeState({ - display_step: 'show_picture', - page: parseInt(evt.target.getAttribute('data-page'), 10) - }); - } - - throw new Error('Unhandled button: ' + evt.target.textContent); - }, false, false) - - ////////////////////////////////////////////////// - // Used when submitting the form - ////////////////////////////////////////////////// - .declareMethod('getContent', function () { - var key, - uuid_key, - result, - gadget = this, - image_list = []; - - for (key in gadget.state) { - if (gadget.state.hasOwnProperty(key)) { - if (key.indexOf("blob_state_") !== -1 && - gadget.state[key] == "OK") { - uuid_key = "blob_uuid_" + key.replace("blob_state_", ""); - image_list.push(gadget.state[uuid_key]); - } - } - } - result = { - data_json: JSON.stringify({ - active_process: gadget.state.active_process, - image_list: image_list, - preferred_cropped_canvas_data: gadget.state.preferred_cropped_canvas_data - }) - }; - return result; - }, {mutex: 'changestate'}) - - .declareMethod('checkValidity', function () { - var gadget = this, - has_thumbnail = false, - key; - for (key in gadget.state) { - if (gadget.state.hasOwnProperty(key)) { - if (key.indexOf("blob_state_") !== -1 && - !gadget.state[key].match("deleted|OK")) { - return false; - } - if (key.indexOf("blob_url_") !== -1) { - if (!gadget.state[key]) { - return false; - } - if (gadget.state[key] && !has_thumbnail) { - has_thumbnail = true; - } - } - } - } - return has_thumbnail; - }, {mutex: 'changestate'}) - - .declareAcquiredMethod("getTranslationList", "getTranslationList"); - -}(rJS, RSVP, window, document, navigator, Cropper, Promise, JSON, jIO, promiseEventListener, domsugar, createImageBitmap, FormData)); \ No newline at end of file +}(rJS, RSVP, window, navigator, Cropper, Promise, JSON, jIO, Caman)); diff --git a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference.xml b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference.xml new file mode 100644 index 00000000000..749538fd3c4 --- /dev/null +++ b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference.xml @@ -0,0 +1,66 @@ + + + + + + + + + + _count + + AAAAAAAAAAI= + + + + _mt_index + + AAAAAAAAAAM= + + + + _tree + + AAAAAAAAAAQ= + + + + description + + + + + + id + DocumentScannerPreference + + + portal_type + Property Sheet + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + diff --git a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_brightness_property.xml b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_brightness_property.xml new file mode 100644 index 00000000000..fe136f1be20 --- /dev/null +++ b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_brightness_property.xml @@ -0,0 +1,59 @@ + + + + + + + + + + _local_properties + + + + + id + mode + + + type + string + + + + + + + categories + + + elementary_type/float + + + + + description + + + + + + id + preferred_image_conversion_brightness_property + + + mode + + + + portal_type + Standard Property + + + preference + 1 + + + + + diff --git a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_compression_property.xml b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_compression_property.xml new file mode 100644 index 00000000000..a376ede9fb4 --- /dev/null +++ b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_compression_property.xml @@ -0,0 +1,59 @@ + + + + + + + + + + _local_properties + + + + + id + mode + + + type + string + + + + + + + categories + + + elementary_type/float + + + + + description + + + + + + id + preferred_image_conversion_compression_property + + + mode + + + + portal_type + Standard Property + + + preference + 1 + + + + + diff --git a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_contrast_property.xml b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_contrast_property.xml new file mode 100644 index 00000000000..495989518a0 --- /dev/null +++ b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_contrast_property.xml @@ -0,0 +1,59 @@ + + + + + + + + + + _local_properties + + + + + id + mode + + + type + string + + + + + + + categories + + + elementary_type/int + + + + + description + + + + + + id + preferred_image_conversion_contrast_property + + + mode + + + + portal_type + Standard Property + + + preference + 1 + + + + + diff --git a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_enable_greyscale_property.xml b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_enable_greyscale_property.xml new file mode 100644 index 00000000000..69e199ca1ef --- /dev/null +++ b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_enable_greyscale_property.xml @@ -0,0 +1,59 @@ + + + + + + + + + + _local_properties + + + + + id + mode + + + type + string + + + + + + + categories + + + elementary_type/boolean + + + + + description + + + + + + id + preferred_image_conversion_enable_greyscale_property + + + mode + + + + portal_type + Standard Property + + + preference + 1 + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredCropperSettingsFromSelection.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredCropperSettingsFromSelection.py index 9be6f0ebb26..629e0d88d6a 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredCropperSettingsFromSelection.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredCropperSettingsFromSelection.py @@ -7,5 +7,4 @@ selection_mapping = portal.portal_selections.getSelectionParamsFor( REQUEST=context.REQUEST) or {} canvas_data = selection_mapping.get(context.REQUEST["HTTP_USER_AGENT"]) or {} - return json.dumps(canvas_data) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.py new file mode 100644 index 00000000000..c03c04f59a4 --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.py @@ -0,0 +1,15 @@ +import json + +portal = context.getPortalObject() +active_preference = portal.portal_preferences.getActiveUserPreference() +if not active_preference: + active_preference = portal.portal_preferences.getActivePreference() + +setting_dict = { + "compression": active_preference.getPreferredImageConversionCompression(), + "enable_greyscale": active_preference.getPreferredImageConversionEnableGreyscale(), + "brightness": active_preference.getPreferredImageConversionBrightness(), + "contrast": active_preference.getPreferredImageConversionContrast(), +} + +return json.dumps(setting_dict) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.xml new file mode 100644 index 00000000000..116a266fbee --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.xml @@ -0,0 +1,62 @@ + + + + + + + + + + Script_magic + 3 + + + _bind_names + + + + + + + + + + _asgns + + + + name_container + container + + + name_context + context + + + name_m_self + script + + + name_subpath + traverse_subpath + + + + + + + + + + + _params + + + + id + Base_getPreferredImageSettingsFromPreference + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeDocumentFromCameraInActiveProcess.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeDocumentFromCameraInActiveProcess.py index 2ff67f187c3..a670922147d 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeDocumentFromCameraInActiveProcess.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_storeDocumentFromCameraInActiveProcess.py @@ -13,7 +13,7 @@ selection_mapping = portal.portal_selections.getSelectionParamsFor( REQUEST=context.REQUEST) or {} http_user_agent = context.REQUEST["HTTP_USER_AGENT"] -selection_mapping[http_user_agent] = preferred_cropped_canvas_data +selection_mapping[http_user_agent] = preferred_cropped_canvas_data or {} portal.portal_selections.setSelectionParamsFor( context.Base_getDocumentScannerSelectionName(), diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml index 4eebff2b607..ae71d97cd2c 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_viewUploadDocumentFromCameraDialog/your_document_scanner_gadget.xml @@ -152,7 +152,7 @@ _text - string: gadget_document_scanner.html + python: [(\'preferred_cropped_canvas_data\', context.Base_getPreferredCropperSettingsFromSelection()), (\'preferred_image_settings_data\', context.Base_getPreferredImageSettingsFromPreference()), ("dialog_method", context.Base_storeDocumentFromCameraInActiveProcess.getId())] diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting.xml new file mode 100644 index 00000000000..59763369779 --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting.xml @@ -0,0 +1,146 @@ + + + + + + + + + + _bind_names + + + + + + + + + + _asgns + + + + + + + + + + + _objects + + + + + + action + Base_edit + + + action_title + + + + description + + + + edit_order + + + + + + encoding + UTF-8 + + + enctype + + + + group_list + + + left + right + center + + + + + groups + + + + center + + + + + + left + + + my_preferred_image_conversion_enable_greyscale + + + + + right + + + my_preferred_image_conversion_compression + my_preferred_image_conversion_brightness + my_preferred_image_conversion_contrast + + + + + + + + id + Preference_viewDocumentScannerSettting + + + method + POST + + + name + Preference_viewDocumentScannerSettting + + + pt + form_view + + + row_length + 4 + + + stored_encoding + UTF-8 + + + title + Document Scanner + + + unicode_mode + 0 + + + update_action + + + + update_action_title + + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_brightness.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_brightness.xml new file mode 100644 index 00000000000..a400a0fa445 --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_brightness.xml @@ -0,0 +1,96 @@ + + + + + + + + + + delegated_list + + + title + + + + + id + my_preferred_image_conversion_brightness + + + message_values + + + + external_validator_failed + The input failed the external validator. + + + + + + overrides + + + + field_id + + + + form_id + + + + target + + + + + + + tales + + + + field_id + + + + form_id + + + + target + + + + + + + values + + + + field_id + my_float_field + + + form_id + Base_viewFieldLibrary + + + target + Click to edit the target + + + title + Brightness [-100 .. 100] + + + + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_compression.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_compression.xml new file mode 100644 index 00000000000..0ae8fbda3dc --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_compression.xml @@ -0,0 +1,96 @@ + + + + + + + + + + delegated_list + + + title + + + + + id + my_preferred_image_conversion_compression + + + message_values + + + + external_validator_failed + The input failed the external validator. + + + + + + overrides + + + + field_id + + + + form_id + + + + target + + + + + + + tales + + + + field_id + + + + form_id + + + + target + + + + + + + values + + + + field_id + my_float_field + + + form_id + Base_viewFieldLibrary + + + target + Click to edit the target + + + title + Image compression [0 .. 1] + + + + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_contrast.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_contrast.xml new file mode 100644 index 00000000000..e8d841d4876 --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_contrast.xml @@ -0,0 +1,96 @@ + + + + + + + + + + delegated_list + + + title + + + + + id + my_preferred_image_conversion_contrast + + + message_values + + + + external_validator_failed + The input failed the external validator. + + + + + + overrides + + + + field_id + + + + form_id + + + + target + + + + + + + tales + + + + field_id + + + + form_id + + + + target + + + + + + + values + + + + field_id + my_integer_value + + + form_id + Base_viewFieldLibrary + + + target + Click to edit the target + + + title + Contrast [-100 .. 100] + + + + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_enable_greyscale.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_enable_greyscale.xml new file mode 100644 index 00000000000..3ba56efd049 --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_enable_greyscale.xml @@ -0,0 +1,96 @@ + + + + + + + + + + delegated_list + + + title + + + + + id + my_preferred_image_conversion_enable_greyscale + + + message_values + + + + external_validator_failed + The input failed the external validator. + + + + + + overrides + + + + field_id + + + + form_id + + + + target + + + + + + + tales + + + + field_id + + + + form_id + + + + target + + + + + + + values + + + + field_id + your_checkbox + + + form_id + Base_viewFieldLibrary + + + target + Click to edit the target + + + title + Enable Greyscale + + + + + + + + diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/caman.full.min.js.js b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/caman.full.min.js.js new file mode 100644 index 00000000000..437fbcabece --- /dev/null +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/caman.full.min.js.js @@ -0,0 +1,251 @@ + +(function(){var $,Analyze,Blender,Calculate,Caman,CamanParser,Canvas,Convert,Event,Fiber,Filter,IO,Image,Layer,Log,Module,Pixel,Plugin,Renderer,Root,Store,Util,fs,http,moduleKeywords,slice,vignetteFilters,__indexOf=[].indexOf||function(item){for(var i=0,l=this.length;i255){return 255;} +return val;};Util.copyAttributes=function(from,to,opts){var attr,_i,_len,_ref,_ref1,_results;if(opts==null){opts={};} +_ref=from.attributes;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){attr=_ref[_i];if((opts.except!=null)&&(_ref1=attr.nodeName,__indexOf.call(opts.except,_ref1)>=0)){continue;} +_results.push(to.setAttribute(attr.nodeName,attr.nodeValue));} +return _results;};Util.dataArray=function(length){if(length==null){length=0;} +if(Caman.NodeJS||(window.Uint8Array!=null)){return new Uint8Array(length);} +return new Array(length);};return Util;})();if(typeof exports!=="undefined"&&exports!==null){Root=exports;Canvas=require('canvas');Image=Canvas.Image;Fiber=require('fibers');fs=require('fs');http=require('http');}else{Root=window;} +Caman=(function(_super){__extends(Caman,_super);Caman.version={release:"4.1.2",date:"7/27/2013"};Caman.DEBUG=false;Caman.allowRevert=true;Caman.crossOrigin="anonymous";Caman.remoteProxy="";Caman.proxyParam="camanProxyUrl";Caman.NodeJS=typeof exports!=="undefined"&&exports!==null;Caman.autoload=!Caman.NodeJS;Caman.toString=function(){return"Version "+Caman.version.release+", Released "+Caman.version.date;};Caman.getAttrId=function(canvas){if(Caman.NodeJS){return true;} +if(typeof canvas==="string"){canvas=$(canvas);} +if(!((canvas!=null)&&(canvas.getAttribute!=null))){return null;} +return canvas.getAttribute('data-caman-id');};function Caman(){this.nodeFileReady=__bind(this.nodeFileReady,this);var args,callback,id,_this=this;if(arguments.length===0){throw"Invalid arguments";} +if(this instanceof Caman){this.finishInit=this.finishInit.bind(this);this.imageLoaded=this.imageLoaded.bind(this);args=arguments[0];if(!Caman.NodeJS){id=parseInt(Caman.getAttrId(args[0]),10);callback=typeof args[1]==="function"?args[1]:typeof args[2]==="function"?args[2]:function(){};if(!isNaN(id)&&Store.has(id)){return Store.execute(id,callback);}} +this.id=Util.uniqid.get();this.initializedPixelData=this.originalPixelData=null;this.cropCoordinates={x:0,y:0};this.cropped=false;this.resized=false;this.pixelStack=[];this.layerStack=[];this.canvasQueue=[];this.currentLayer=null;this.scaled=false;this.analyze=new Analyze(this);this.renderer=new Renderer(this);this.domIsLoaded(function(){_this.parseArguments(args);return _this.setup();});return this;}else{return new Caman(arguments);}} +Caman.prototype.domIsLoaded=function(cb){var listener,_this=this;if(Caman.NodeJS){return setTimeout(function(){return cb.call(_this);},0);}else{if(document.readyState==="complete"){Log.debug("DOM initialized");return setTimeout(function(){return cb.call(_this);},0);}else{listener=function(){if(document.readyState==="complete"){Log.debug("DOM initialized");return cb.call(_this);}};return document.addEventListener("readystatechange",listener,false);}}};Caman.prototype.parseArguments=function(args){var key,val,_ref,_results;if(args.length===0){throw"Invalid arguments given";} +this.initObj=null;this.initType=null;this.imageUrl=null;this.callback=function(){};this.setInitObject(args[0]);if(args.length===1){return;} +switch(typeof args[1]){case"string":this.imageUrl=args[1];break;case"function":this.callback=args[1];} +if(args.length===2){return;} +this.callback=args[2];if(args.length===4){_ref=args[4];_results=[];for(key in _ref){if(!__hasProp.call(_ref,key))continue;val=_ref[key];_results.push(this.options[key]=val);} +return _results;}};Caman.prototype.setInitObject=function(obj){if(Caman.NodeJS){this.initObj=obj;this.initType='node';return;} +if(typeof obj==="object"){this.initObj=obj;}else{this.initObj=$(obj);} +if(this.initObj==null){throw"Could not find image or canvas for initialization.";} +return this.initType=this.initObj.nodeName.toLowerCase();};Caman.prototype.setup=function(){switch(this.initType){case"node":return this.initNode();case"img":return this.initImage();case"canvas":return this.initCanvas();}};Caman.prototype.initNode=function(){Log.debug("Initializing for NodeJS");if(typeof this.initObj==="string"&&this.initObj.match(/^https?:\/\//)){return this.readFromHttp(this.initObj,this.nodeFileReady);}else if(typeof this.initObj==="string"){return fs.readFile(this.initObj,this.nodeFileReady);}else{return this.nodeFileReady(null,this.initObj);}};Caman.prototype.readFromHttp=function(url,callback){var req;Log.debug("Fetching image from "+url);req=http.get(url,function(res){var buf;buf='';res.setEncoding('binary');res.on('data',function(chunk){return buf+=chunk;});return res.on('end',function(){return callback(null,new Buffer(buf,'binary'));});});return req.on('error',callback);};Caman.prototype.nodeFileReady=function(err,data){if(err){throw err;} +this.image=new Image();this.image.src=data;Log.debug("Image loaded. Width = "+(this.imageWidth())+", Height = "+(this.imageHeight()));this.canvas=new Canvas(this.imageWidth(),this.imageHeight());return this.finishInit();};Caman.prototype.initImage=function(){this.image=this.initObj;this.canvas=document.createElement('canvas');this.context=this.canvas.getContext('2d');Util.copyAttributes(this.image,this.canvas,{except:['src']});if(this.image.parentNode!=null){this.image.parentNode.replaceChild(this.canvas,this.image);} +this.imageAdjustments();return this.waitForImageLoaded();};Caman.prototype.initCanvas=function(){this.canvas=this.initObj;this.context=this.canvas.getContext('2d');if(this.imageUrl!=null){this.image=document.createElement('img');this.image.src=this.imageUrl;this.imageAdjustments();return this.waitForImageLoaded();}else{return this.finishInit();}};Caman.prototype.imageAdjustments=function(){if(this.needsHiDPISwap()){Log.debug(this.image.src,"->",this.hiDPIReplacement());this.swapped=true;this.image.src=this.hiDPIReplacement();} +if(IO.isRemote(this.image)){this.image.src=IO.proxyUrl(this.image.src);return Log.debug("Remote image detected, using URL = "+this.image.src);}};Caman.prototype.waitForImageLoaded=function(){if(this.isImageLoaded()){return this.imageLoaded();}else{return this.image.onload=this.imageLoaded;}};Caman.prototype.isImageLoaded=function(){if(!this.image.complete){return false;} +if((this.image.naturalWidth!=null)&&this.image.naturalWidth===0){return false;} +return true;};Caman.prototype.imageWidth=function(){return this.image.width||this.image.naturalWidth;};Caman.prototype.imageHeight=function(){return this.image.height||this.image.naturalHeight;};Caman.prototype.imageLoaded=function(){Log.debug("Image loaded. Width = "+(this.imageWidth())+", Height = "+(this.imageHeight()));if(this.swapped){this.canvas.width=this.imageWidth()/this.hiDPIRatio();this.canvas.height=this.imageHeight()/this.hiDPIRatio();}else{this.canvas.width=this.imageWidth();this.canvas.height=this.imageHeight();} +return this.finishInit();};Caman.prototype.finishInit=function(){var i,pixel,_i,_len,_ref;if(this.context==null){this.context=this.canvas.getContext('2d');} +this.originalWidth=this.preScaledWidth=this.width=this.canvas.width;this.originalHeight=this.preScaledHeight=this.height=this.canvas.height;this.hiDPIAdjustments();if(!this.hasId()){this.assignId();} +if(this.image!=null){this.context.drawImage(this.image,0,0,this.imageWidth(),this.imageHeight(),0,0,this.preScaledWidth,this.preScaledHeight);} +this.imageData=this.context.getImageData(0,0,this.canvas.width,this.canvas.height);this.pixelData=this.imageData.data;if(Caman.allowRevert){this.initializedPixelData=Util.dataArray(this.pixelData.length);this.originalPixelData=Util.dataArray(this.pixelData.length);_ref=this.pixelData;for(i=_i=0,_len=_ref.length;_i<_len;i=++_i){pixel=_ref[i];this.initializedPixelData[i]=pixel;this.originalPixelData[i]=pixel;}} +this.dimensions={width:this.canvas.width,height:this.canvas.height};if(!Caman.NodeJS){Store.put(this.id,this);} +this.callback.call(this,this);return this.callback=function(){};};Caman.prototype.reloadCanvasData=function(){this.imageData=this.context.getImageData(0,0,this.canvas.width,this.canvas.height);return this.pixelData=this.imageData.data;};Caman.prototype.resetOriginalPixelData=function(){var i,pixel,_i,_len,_ref,_results;if(!Caman.allowRevert){throw"Revert disabled";} +this.originalPixelData=Util.dataArray(this.pixelData.length);_ref=this.pixelData;_results=[];for(i=_i=0,_len=_ref.length;_i<_len;i=++_i){pixel=_ref[i];_results.push(this.originalPixelData[i]=pixel);} +return _results;};Caman.prototype.hasId=function(){return Caman.getAttrId(this.canvas)!=null;};Caman.prototype.assignId=function(){if(Caman.NodeJS||this.canvas.getAttribute('data-caman-id')){return;} +return this.canvas.setAttribute('data-caman-id',this.id);};Caman.prototype.hiDPIDisabled=function(){return this.canvas.getAttribute('data-caman-hidpi-disabled')!==null;};Caman.prototype.hiDPIAdjustments=function(){var ratio;if(Caman.NodeJS||!this.needsHiDPISwap()){return;} +ratio=this.hiDPIRatio();if(ratio!==1){Log.debug("HiDPI ratio = "+ratio);this.scaled=true;this.preScaledWidth=this.canvas.width;this.preScaledHeight=this.canvas.height;this.canvas.width=this.preScaledWidth*ratio;this.canvas.height=this.preScaledHeight*ratio;this.canvas.style.width=""+this.preScaledWidth+"px";this.canvas.style.height=""+this.preScaledHeight+"px";this.context.scale(ratio,ratio);this.width=this.originalWidth=this.canvas.width;return this.height=this.originalHeight=this.canvas.height;}};Caman.prototype.hiDPIRatio=function(){var backingStoreRatio,devicePixelRatio;devicePixelRatio=window.devicePixelRatio||1;backingStoreRatio=this.context.webkitBackingStorePixelRatio||this.context.mozBackingStorePixelRatio||this.context.msBackingStorePixelRatio||this.context.oBackingStorePixelRatio||this.context.backingStorePixelRatio||1;return devicePixelRatio/backingStoreRatio;};Caman.prototype.hiDPICapable=function(){return(window.devicePixelRatio!=null)&&window.devicePixelRatio!==1;};Caman.prototype.needsHiDPISwap=function(){if(this.hiDPIDisabled()||!this.hiDPICapable()){return false;} +return this.hiDPIReplacement()!==null;};Caman.prototype.hiDPIReplacement=function(){if(this.image==null){return null;} +return this.image.getAttribute('data-caman-hidpi');};Caman.prototype.replaceCanvas=function(newCanvas){var oldCanvas;oldCanvas=this.canvas;this.canvas=newCanvas;this.context=this.canvas.getContext('2d');if(!Caman.NodeJS){oldCanvas.parentNode.replaceChild(this.canvas,oldCanvas);} +this.width=this.canvas.width;this.height=this.canvas.height;this.reloadCanvasData();return this.dimensions={width:this.canvas.width,height:this.canvas.height};};Caman.prototype.render=function(callback){var _this=this;if(callback==null){callback=function(){};} +Event.trigger(this,"renderStart");return this.renderer.execute(function(){_this.context.putImageData(_this.imageData,0,0);return callback.call(_this);});};Caman.prototype.revert=function(updateContext){var i,pixel,_i,_len,_ref;if(updateContext==null){updateContext=true;} +if(!Caman.allowRevert){throw"Revert disabled";} +_ref=this.originalVisiblePixels();for(i=_i=0,_len=_ref.length;_i<_len;i=++_i){pixel=_ref[i];this.pixelData[i]=pixel;} +if(updateContext){return this.context.putImageData(this.imageData,0,0);}};Caman.prototype.reset=function(){var canvas,ctx,i,imageData,pixel,pixelData,_i,_len,_ref;canvas=document.createElement('canvas');Util.copyAttributes(this.canvas,canvas);canvas.width=this.originalWidth;canvas.height=this.originalHeight;ctx=canvas.getContext('2d');imageData=ctx.getImageData(0,0,canvas.width,canvas.height);pixelData=imageData.data;_ref=this.initializedPixelData;for(i=_i=0,_len=_ref.length;_i<_len;i=++_i){pixel=_ref[i];pixelData[i]=pixel;} +ctx.putImageData(imageData,0,0);this.cropCoordinates={x:0,y:0};this.resized=false;return this.replaceCanvas(canvas);};Caman.prototype.originalVisiblePixels=function(){var canvas,coord,ctx,endX,endY,i,imageData,pixel,pixelData,pixels,scaledCanvas,startX,startY,width,_i,_j,_len,_ref,_ref1,_ref2,_ref3;if(!Caman.allowRevert){throw"Revert disabled";} +pixels=[];startX=this.cropCoordinates.x;endX=startX+this.width;startY=this.cropCoordinates.y;endY=startY+this.height;if(this.resized){canvas=document.createElement('canvas');canvas.width=this.originalWidth;canvas.height=this.originalHeight;ctx=canvas.getContext('2d');imageData=ctx.getImageData(0,0,canvas.width,canvas.height);pixelData=imageData.data;_ref=this.originalPixelData;for(i=_i=0,_len=_ref.length;_i<_len;i=++_i){pixel=_ref[i];pixelData[i]=pixel;} +ctx.putImageData(imageData,0,0);scaledCanvas=document.createElement('canvas');scaledCanvas.width=this.width;scaledCanvas.height=this.height;ctx=scaledCanvas.getContext('2d');ctx.drawImage(canvas,0,0,this.originalWidth,this.originalHeight,0,0,this.width,this.height);pixelData=ctx.getImageData(0,0,this.width,this.height).data;width=this.width;}else{pixelData=this.originalPixelData;width=this.originalWidth;} +for(i=_j=0,_ref1=pixelData.length;_j<_ref1;i=_j+=4){coord=Pixel.locationToCoordinates(i,width);if(((startX<=(_ref2=coord.x)&&_ref2_ref;i=0<=_ref?++_i:--_i){divisor+=adjust[i];}} +this.renderer.add({type:Filter.Type.Kernel,name:name,adjust:adjust,divisor:divisor,bias:bias});return this;};Caman.prototype.processPlugin=function(plugin,args){this.renderer.add({type:Filter.Type.Plugin,plugin:plugin,args:args});return this;};Caman.prototype.newLayer=function(callback){var layer;layer=new Layer(this);this.canvasQueue.push(layer);this.renderer.add({type:Filter.Type.LayerDequeue});callback.call(layer);this.renderer.add({type:Filter.Type.LayerFinished});return this;};Caman.prototype.executeLayer=function(layer){return this.pushContext(layer);};Caman.prototype.pushContext=function(layer){this.layerStack.push(this.currentLayer);this.pixelStack.push(this.pixelData);this.currentLayer=layer;return this.pixelData=layer.pixelData;};Caman.prototype.popContext=function(){this.pixelData=this.pixelStack.pop();return this.currentLayer=this.layerStack.pop();};Caman.prototype.applyCurrentLayer=function(){return this.currentLayer.applyToParent();};return Caman;})(Module);Root.Caman=Caman;Caman.Analyze=(function(){function Analyze(c){this.c=c;} +Analyze.prototype.calculateLevels=function(){var i,levels,numPixels,_i,_j,_k,_ref;levels={r:{},g:{},b:{}};for(i=_i=0;_i<=255;i=++_i){levels.r[i]=0;levels.g[i]=0;levels.b[i]=0;} +for(i=_j=0,_ref=this.c.pixelData.length;_j<_ref;i=_j+=4){levels.r[this.c.pixelData[i]]++;levels.g[this.c.pixelData[i+1]]++;levels.b[this.c.pixelData[i+2]]++;} +numPixels=this.c.pixelData.length/4;for(i=_k=0;_k<=255;i=++_k){levels.r[i]/=numPixels;levels.g[i]/=numPixels;levels.b[i]/=numPixels;} +return levels;};return Analyze;})();Analyze=Caman.Analyze;Caman.DOMUpdated=function(){var img,imgs,parser,_i,_len,_results;imgs=document.querySelectorAll("img[data-caman]");if(!(imgs.length>0)){return;} +_results=[];for(_i=0,_len=imgs.length;_i<_len;_i++){img=imgs[_i];_results.push(parser=new CamanParser(img,function(){this.parse();return this.execute();}));} +return _results;};if(Caman.autoload){(function(){if(document.readyState==="complete"){return Caman.DOMUpdated();}else{return document.addEventListener("DOMContentLoaded",Caman.DOMUpdated,false);}})();} +CamanParser=(function(){var INST_REGEX;INST_REGEX="(\\w+)\\((.*?)\\)";function CamanParser(ele,ready){this.dataStr=ele.getAttribute('data-caman');this.caman=Caman(ele,ready.bind(this));} +CamanParser.prototype.parse=function(){var args,e,filter,func,inst,instFunc,m,r,unparsedInstructions,_i,_len,_ref,_results;this.ele=this.caman.canvas;r=new RegExp(INST_REGEX,'g');unparsedInstructions=this.dataStr.match(r);if(!(unparsedInstructions.length>0)){return;} +r=new RegExp(INST_REGEX);_results=[];for(_i=0,_len=unparsedInstructions.length;_i<_len;_i++){inst=unparsedInstructions[_i];_ref=inst.match(r),m=_ref[0],filter=_ref[1],args=_ref[2];instFunc=new Function("return function() { this."+filter+"("+args+"); };");try{func=instFunc();_results.push(func.call(this.caman));}catch(_error){e=_error;_results.push(Log.debug(e));}} +return _results;};CamanParser.prototype.execute=function(){var ele;ele=this.ele;return this.caman.render(function(){return ele.parentNode.replaceChild(this.toImage(),ele);});};return CamanParser;})();Caman.Blender=(function(){function Blender(){} +Blender.blenders={};Blender.register=function(name,func){return this.blenders[name]=func;};Blender.execute=function(name,rgbaLayer,rgbaParent){return this.blenders[name](rgbaLayer,rgbaParent);};return Blender;})();Blender=Caman.Blender;Caman.Calculate=(function(){function Calculate(){} +Calculate.distance=function(x1,y1,x2,y2){return Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));};Calculate.randomRange=function(min,max,getFloat){var rand;if(getFloat==null){getFloat=false;} +rand=min+(Math.random()*(max-min));if(getFloat){return rand.toFixed(getFloat);}else{return Math.round(rand);}};Calculate.luminance=function(rgba){return(0.299*rgba.r)+(0.587*rgba.g)+(0.114*rgba.b);};Calculate.bezier=function(start,ctrl1,ctrl2,end,lowBound,highBound){var bezier,clamp,controlPoints,endX,i,j,lerp,next,prev,t,_i,_j,_ref;if(lowBound==null){lowBound=0;} +if(highBound==null){highBound=255;} +if(start[0]instanceof Array){controlPoints=start;lowBound=ctrl1;highBound=ctrl2;}else{controlPoints=[start,ctrl1,ctrl2,end];} +if(controlPoints.length<2){throw"Invalid number of arguments to bezier";} +bezier={};lerp=function(a,b,t){return a*(1-t)+b*t;};clamp=function(a,min,max){return Math.min(Math.max(a,min),max);};for(i=_i=0;_i<1000;i=++_i){t=i/1000;prev=controlPoints;while(prev.length>1){next=[];for(j=_j=0,_ref=prev.length-2;0<=_ref?_j<=_ref:_j>=_ref;j=0<=_ref?++_j:--_j){next.push([lerp(prev[j][0],prev[j+1][0],t),lerp(prev[j][1],prev[j+1][1],t)]);} +prev=next;} +bezier[Math.round(prev[0][0])]=Math.round(clamp(prev[0][1],lowBound,highBound));} +endX=controlPoints[controlPoints.length-1][0];bezier=Caman.Calculate.missingValues(bezier,endX);if(bezier[endX]==null){bezier[endX]=bezier[endX-1];} +return bezier;};Calculate.hermite=function(controlPoints,lowBound,highBound){var add,clamp,count,endX,fac0,fac1,fac2,fac3,i,j,lerp,m0,m1,mul,p,p0,p1,pointsPerSegment,pointsPerStep,pos,ret,sub,t,_i,_j,_ref,_this=this;if(controlPoints.length<2){throw"Invalid number of arguments to hermite";} +ret={};lerp=function(a,b,t){return a*(1-t)+b*t;};add=function(a,b,c,d){return[a[0]+b[0]+c[0]+d[0],a[1]+b[1]+c[1]+d[1]];};mul=function(a,b){return[a[0]*b[0],a[1]*b[1]];};sub=function(a,b){return[a[0]-b[0],a[1]-b[1]];};clamp=function(a,min,max){return Math.min(Math.max(a,min),max);};count=0;for(i=_i=0,_ref=controlPoints.length-2;0<=_ref?_i<=_ref:_i>=_ref;i=0<=_ref?++_i:--_i){p0=controlPoints[i];p1=controlPoints[i+1];pointsPerSegment=p1[0]-p0[0];pointsPerStep=1/pointsPerSegment;if(i===controlPoints.length-2){pointsPerStep=1/(pointsPerSegment-1);} +p=i>0?controlPoints[i-1]:p0;m0=mul(sub(p1,p),[0.5,0.5]);p=i=pointsPerSegment;j=0<=pointsPerSegment?++_j:--_j){t=j*pointsPerStep;fac0=2.0*t*t*t-3.0*t*t+1.0;fac1=t*t*t-2.0*t*t+t;fac2=-2.0*t*t*t+3.0*t*t;fac3=t*t*t-t*t;pos=add(mul(p0,[fac0,fac0]),mul(m0,[fac1,fac1]),mul(p1,[fac2,fac2]),mul(m1,[fac3,fac3]));ret[Math.round(pos[0])]=Math.round(clamp(pos[1],lowBound,highBound));count+=1;}} +endX=controlPoints[controlPoints.length-1][0];ret=Caman.Calculate.missingValues(ret,endX);return ret;};Calculate.missingValues=function(values,endX){var i,j,leftCoord,ret,rightCoord,_i,_j;if(Object.keys(values).length=endX;i=0<=endX?++_i:--_i){if(values[i]!=null){ret[i]=values[i];}else{leftCoord=[i-1,ret[i-1]];for(j=_j=i;i<=endX?_j<=endX:_j>=endX;j=i<=endX?++_j:--_j){if(values[j]!=null){rightCoord=[j,values[j]];break;}} +ret[i]=leftCoord[1]+((rightCoord[1]-leftCoord[1])/(rightCoord[0]-leftCoord[0]))*(i-leftCoord[0]);}} +return ret;} +return values;};return Calculate;})();Calculate=Caman.Calculate;Caman.Convert=(function(){function Convert(){} +Convert.hexToRGB=function(hex){var b,g,r;if(hex.charAt(0)==="#"){hex=hex.substr(1);} +r=parseInt(hex.substr(0,2),16);g=parseInt(hex.substr(2,2),16);b=parseInt(hex.substr(4,2),16);return{r:r,g:g,b:b};};Convert.rgbToHSL=function(r,g,b){var d,h,l,max,min,s;if(typeof r==="object"){g=r.g;b=r.b;r=r.r;} +r/=255;g/=255;b/=255;max=Math.max(r,g,b);min=Math.min(r,g,b);l=(max+min)/2;if(max===min){h=s=0;}else{d=max-min;s=l>0.5?d/(2-max-min):d/(max+min);h=(function(){switch(max){case r:return(g-b)/d+(g1){t-=1;} +if(t<1/6){return p+(q-p)*6*t;} +if(t<1/2){return q;} +if(t<2/3){return p+(q-p)*(2/3-t)*6;} +return p;};Convert.rgbToHSV=function(r,g,b){var d,h,max,min,s,v;r/=255;g/=255;b/=255;max=Math.max(r,g,b);min=Math.min(r,g,b);v=max;d=max-min;s=max===0?0:d/max;if(max===min){h=0;}else{h=(function(){switch(max){case r:return(g-b)/d+(g0.04045){r=Math.pow((r+0.055)/1.055,2.4);}else{r/=12.92;} +if(g>0.04045){g=Math.pow((g+0.055)/1.055,2.4);}else{g/=12.92;} +if(b>0.04045){b=Math.pow((b+0.055)/1.055,2.4);}else{b/=12.92;} +x=r*0.4124+g*0.3576+b*0.1805;y=r*0.2126+g*0.7152+b*0.0722;z=r*0.0193+g*0.1192+b*0.9505;return{x:x*100,y:y*100,z:z*100};};Convert.xyzToRGB=function(x,y,z){var b,g,r;x/=100;y/=100;z/=100;r=(3.2406*x)+(-1.5372*y)+(-0.4986*z);g=(-0.9689*x)+(1.8758*y)+(0.0415*z);b=(0.0557*x)+(-0.2040*y)+(1.0570*z);if(r>0.0031308){r=(1.055*Math.pow(r,0.4166666667))-0.055;}else{r*=12.92;} +if(g>0.0031308){g=(1.055*Math.pow(g,0.4166666667))-0.055;}else{g*=12.92;} +if(b>0.0031308){b=(1.055*Math.pow(b,0.4166666667))-0.055;}else{b*=12.92;} +return{r:r*255,g:g*255,b:b*255};};Convert.xyzToLab=function(x,y,z){var a,b,l,whiteX,whiteY,whiteZ;if(typeof x==="object"){y=x.y;z=x.z;x=x.x;} +whiteX=95.047;whiteY=100.0;whiteZ=108.883;x/=whiteX;y/=whiteY;z/=whiteZ;if(x>0.008856451679){x=Math.pow(x,0.3333333333);}else{x=(7.787037037*x)+0.1379310345;} +if(y>0.008856451679){y=Math.pow(y,0.3333333333);}else{y=(7.787037037*y)+0.1379310345;} +if(z>0.008856451679){z=Math.pow(z,0.3333333333);}else{z=(7.787037037*z)+0.1379310345;} +l=116*y-16;a=500*(x-y);b=200*(y-z);return{l:l,a:a,b:b};};Convert.labToXYZ=function(l,a,b){var x,y,z;if(typeof l==="object"){a=l.a;b=l.b;l=l.l;} +y=(l+16)/116;x=y+(a/500);z=y-(b/200);if(x>0.2068965517){x=x*x*x;}else{x=0.1284185493*(x-0.1379310345);} +if(y>0.2068965517){y=y*y*y;}else{y=0.1284185493*(y-0.1379310345);} +if(z>0.2068965517){z=z*z*z;}else{z=0.1284185493*(z-0.1379310345);} +return{x:x*95.047,y:y*100.0,z:z*108.883};};Convert.rgbToLab=function(r,g,b){var xyz;if(typeof r==="object"){g=r.g;b=r.b;r=r.r;} +xyz=this.rgbToXYZ(r,g,b);return this.xyzToLab(xyz);};Convert.labToRGB=function(l,a,b){};return Convert;})();Convert=Caman.Convert;Caman.Event=(function(){function Event(){} +Event.events={};Event.types=["processStart","processComplete","renderStart","renderFinished","blockStarted","blockFinished"];Event.trigger=function(target,type,data){var event,_i,_len,_ref,_results;if(data==null){data=null;} +if(this.events[type]&&this.events[type].length){_ref=this.events[type];_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){event=_ref[_i];if(event.target===null||target.id===event.target.id){_results.push(event.fn.call(target,data));}else{_results.push(void 0);}} +return _results;}};Event.listen=function(target,type,fn){var _fn,_type;if(typeof target==="string"){_type=target;_fn=type;target=null;type=_type;fn=_fn;} +if(__indexOf.call(this.types,type)<0){return false;} +if(!this.events[type]){this.events[type]=[];} +this.events[type].push({target:target,fn:fn});return true;};return Event;})();Event=Caman.Event;Caman.Filter=(function(){function Filter(){} +Filter.Type={Single:1,Kernel:2,LayerDequeue:3,LayerFinished:4,LoadOverlay:5,Plugin:6};Filter.register=function(name,filterFunc){return Caman.prototype[name]=filterFunc;};return Filter;})();Filter=Caman.Filter;Caman.IO=(function(){function IO(){} +IO.domainRegex=/(?:(?:http|https):\/\/)((?:\w+)\.(?:(?:\w|\.)+))/;IO.isRemote=function(img){if(img==null){return false;} +if(this.corsEnabled(img)){return false;} +return this.isURLRemote(img.src);};IO.corsEnabled=function(img){var _ref;return(img.crossOrigin!=null)&&((_ref=img.crossOrigin.toLowerCase())==='anonymous'||_ref==='use-credentials');};IO.isURLRemote=function(url){var matches;matches=url.match(this.domainRegex);if(matches){return matches[1]!==document.domain;}else{return false;}};IO.remoteCheck=function(src){if(this.isURLRemote(src)){if(!Caman.remoteProxy.length){Log.info("Attempting to load a remote image without a configured proxy. URL: "+src);}else{if(Caman.isURLRemote(Caman.remoteProxy)){Log.info("Cannot use a remote proxy for loading images.");return;} +return this.proxyUrl(src);}}};IO.proxyUrl=function(src){return""+Caman.remoteProxy+"?"+Caman.proxyParam+"="+(encodeURIComponent(src));};IO.useProxy=function(lang){var langToExt;langToExt={ruby:'rb',python:'py',perl:'pl',javascript:'js'};lang=lang.toLowerCase();if(langToExt[lang]!=null){lang=langToExt[lang];} +return"proxies/caman_proxy."+lang;};return IO;})();Caman.prototype.save=function(){if(typeof exports!=="undefined"&&exports!==null){return this.nodeSave.apply(this,arguments);}else{return this.browserSave.apply(this,arguments);}};Caman.prototype.browserSave=function(type){var image;if(type==null){type="png";} +type=type.toLowerCase();image=this.toBase64(type).replace("image/"+type,"image/octet-stream");return document.location.href=image;};Caman.prototype.nodeSave=function(file,overwrite,callback){var e,stats;if(overwrite==null){overwrite=true;} +if(callback==null){callback=null;} +try{stats=fs.statSync(file);if(stats.isFile()&&!overwrite){return false;}}catch(_error){e=_error;Log.debug("Creating output file "+file);} +return fs.writeFile(file,this.canvas.toBuffer(),function(err){Log.debug("Finished writing to "+file);if(callback){return callback.call(this,err);}});};Caman.prototype.toImage=function(type){var img;img=new Image();img.src=this.toBase64(type);img.width=this.dimensions.width;img.height=this.dimensions.height;if(window.devicePixelRatio){img.width/=window.devicePixelRatio;img.height/=window.devicePixelRatio;} +return img;};Caman.prototype.toBase64=function(type){if(type==null){type="png";} +type=type.toLowerCase();return this.canvas.toDataURL("image/"+type);};IO=Caman.IO;Caman.Layer=(function(){function Layer(c){this.c=c;this.filter=this.c;this.options={blendingMode:'normal',opacity:1.0};this.layerID=Util.uniqid.get();this.canvas=typeof exports!=="undefined"&&exports!==null?new Canvas():document.createElement('canvas');this.canvas.width=this.c.dimensions.width;this.canvas.height=this.c.dimensions.height;this.context=this.canvas.getContext('2d');this.context.createImageData(this.canvas.width,this.canvas.height);this.imageData=this.context.getImageData(0,0,this.canvas.width,this.canvas.height);this.pixelData=this.imageData.data;} +Layer.prototype.newLayer=function(cb){return this.c.newLayer.call(this.c,cb);};Layer.prototype.setBlendingMode=function(mode){this.options.blendingMode=mode;return this;};Layer.prototype.opacity=function(opacity){this.options.opacity=opacity/100;return this;};Layer.prototype.copyParent=function(){var i,parentData,_i,_ref;parentData=this.c.pixelData;for(i=_i=0,_ref=this.c.pixelData.length;_i<_ref;i=_i+=4){this.pixelData[i]=parentData[i];this.pixelData[i+1]=parentData[i+1];this.pixelData[i+2]=parentData[i+2];this.pixelData[i+3]=parentData[i+3];} +return this;};Layer.prototype.fillColor=function(){return this.c.fillColor.apply(this.c,arguments);};Layer.prototype.overlayImage=function(image){if(typeof image==="object"){image=image.src;}else if(typeof image==="string"&&image[0]==="#"){image=$(image).src;} +if(!image){return this;} +this.c.renderer.renderQueue.push({type:Filter.Type.LoadOverlay,src:image,layer:this});return this;};Layer.prototype.applyToParent=function(){var i,layerData,parentData,result,rgbaLayer,rgbaParent,_i,_ref,_results;parentData=this.c.pixelStack[this.c.pixelStack.length-1];layerData=this.c.pixelData;_results=[];for(i=_i=0,_ref=layerData.length;_i<_ref;i=_i+=4){rgbaParent={r:parentData[i],g:parentData[i+1],b:parentData[i+2],a:parentData[i+3]};rgbaLayer={r:layerData[i],g:layerData[i+1],b:layerData[i+2],a:layerData[i+3]};result=Blender.execute(this.options.blendingMode,rgbaLayer,rgbaParent);result.r=Util.clampRGB(result.r);result.g=Util.clampRGB(result.g);result.b=Util.clampRGB(result.b);if(result.a==null){result.a=rgbaLayer.a;} +parentData[i]=rgbaParent.r-((rgbaParent.r-result.r)*(this.options.opacity*(result.a/255)));parentData[i+1]=rgbaParent.g-((rgbaParent.g-result.g)*(this.options.opacity*(result.a/255)));_results.push(parentData[i+2]=rgbaParent.b-((rgbaParent.b-result.b)*(this.options.opacity*(result.a/255))));} +return _results;};return Layer;})();Layer=Caman.Layer;Caman.Logger=(function(){function Logger(){var name,_i,_len,_ref;_ref=['log','info','warn','error'];for(_i=0,_len=_ref.length;_i<_len;_i++){name=_ref[_i];this[name]=(function(name){return function(){var args,e;args=1<=arguments.length?__slice.call(arguments,0):[];if(!Caman.DEBUG){return;} +try{return console[name].apply(console,args);}catch(_error){e=_error;return console[name](args);}};})(name);} +this.debug=this.log;} +return Logger;})();Log=new Caman.Logger();Caman.Pixel=(function(){Pixel.coordinatesToLocation=function(x,y,width){return(y*width+x)*4;};Pixel.locationToCoordinates=function(loc,width){var x,y;y=Math.floor(loc/(width*4));x=(loc%(width*4))/4;return{x:x,y:y};};function Pixel(r,g,b,a,c){this.r=r!=null?r:0;this.g=g!=null?g:0;this.b=b!=null?b:0;this.a=a!=null?a:255;this.c=c!=null?c:null;this.loc=0;} +Pixel.prototype.setContext=function(c){return this.c=c;};Pixel.prototype.locationXY=function(){var x,y;if(this.c==null){throw"Requires a CamanJS context";} +y=this.c.dimensions.height-Math.floor(this.loc/(this.c.dimensions.width*4));x=(this.loc%(this.c.dimensions.width*4))/4;return{x:x,y:y};};Pixel.prototype.pixelAtLocation=function(loc){if(this.c==null){throw"Requires a CamanJS context";} +return new Pixel(this.c.pixelData[loc],this.c.pixelData[loc+1],this.c.pixelData[loc+2],this.c.pixelData[loc+3],this.c);};Pixel.prototype.getPixelRelative=function(horiz,vert){var newLoc;if(this.c==null){throw"Requires a CamanJS context";} +newLoc=this.loc+(this.c.dimensions.width*4*(vert*-1))+(4*horiz);if(newLoc>this.c.pixelData.length||newLoc<0){return new Pixel(0,0,0,255,this.c);} +return this.pixelAtLocation(newLoc);};Pixel.prototype.putPixelRelative=function(horiz,vert,rgba){var nowLoc;if(this.c==null){throw"Requires a CamanJS context";} +nowLoc=this.loc+(this.c.dimensions.width*4*(vert*-1))+(4*horiz);if(newLoc>this.c.pixelData.length||newLoc<0){return;} +this.c.pixelData[newLoc]=rgba.r;this.c.pixelData[newLoc+1]=rgba.g;this.c.pixelData[newLoc+2]=rgba.b;this.c.pixelData[newLoc+3]=rgba.a;return true;};Pixel.prototype.getPixel=function(x,y){var loc;if(this.c==null){throw"Requires a CamanJS context";} +loc=this.coordinatesToLocation(x,y,this.width);return this.pixelAtLocation(loc);};Pixel.prototype.putPixel=function(x,y,rgba){var loc;if(this.c==null){throw"Requires a CamanJS context";} +loc=this.coordinatesToLocation(x,y,this.width);this.c.pixelData[loc]=rgba.r;this.c.pixelData[loc+1]=rgba.g;this.c.pixelData[loc+2]=rgba.b;return this.c.pixelData[loc+3]=rgba.a;};Pixel.prototype.toString=function(){return this.toKey();};Pixel.prototype.toHex=function(includeAlpha){var hex;if(includeAlpha==null){includeAlpha=false;} +hex='#'+this.r.toString(16)+this.g.toString(16)+this.b.toString(16);if(includeAlpha){return hex+this.a.toString(16);}else{return hex;}};return Pixel;})();Pixel=Caman.Pixel;Caman.Plugin=(function(){function Plugin(){} +Plugin.plugins={};Plugin.register=function(name,plugin){return this.plugins[name]=plugin;};Plugin.execute=function(context,name,args){return this.plugins[name].apply(context,args);};return Plugin;})();Plugin=Caman.Plugin;Caman.Renderer=(function(){Renderer.Blocks=Caman.NodeJS?require('os').cpus().length:4;function Renderer(c){this.c=c;this.processNext=__bind(this.processNext,this);this.renderQueue=[];this.modPixelData=null;} +Renderer.prototype.add=function(job){if(job==null){return;} +return this.renderQueue.push(job);};Renderer.prototype.processNext=function(){var layer;if(this.renderQueue.length===0){Event.trigger(this,"renderFinished");if(this.finishedFn!=null){this.finishedFn.call(this.c);} +return this;} +this.currentJob=this.renderQueue.shift();switch(this.currentJob.type){case Filter.Type.LayerDequeue:layer=this.c.canvasQueue.shift();this.c.executeLayer(layer);return this.processNext();case Filter.Type.LayerFinished:this.c.applyCurrentLayer();this.c.popContext();return this.processNext();case Filter.Type.LoadOverlay:return this.loadOverlay(this.currentJob.layer,this.currentJob.src);case Filter.Type.Plugin:return this.executePlugin();default:return this.executeFilter();}};Renderer.prototype.execute=function(callback){this.finishedFn=callback;this.modPixelData=Util.dataArray(this.c.pixelData.length);return this.processNext();};Renderer.prototype.eachBlock=function(fn){var blockN,blockPixelLength,bnum,end,f,i,lastBlockN,n,start,_i,_ref,_results,_this=this;this.blocksDone=0;n=this.c.pixelData.length;blockPixelLength=Math.floor((n/4)/Renderer.Blocks);blockN=blockPixelLength*4;lastBlockN=blockN+((n/4)%Renderer.Blocks)*4;_results=[];for(i=_i=0,_ref=Renderer.Blocks;0<=_ref?_i<_ref:_i>_ref;i=0<=_ref?++_i:--_i){start=i*blockN;end=start+(i===Renderer.Blocks-1?lastBlockN:blockN);if(Caman.NodeJS){f=Fiber(function(){return fn.call(_this,i,start,end);});bnum=f.run();_results.push(this.blockFinished(bnum));}else{_results.push(setTimeout((function(i,start,end){return function(){return fn.call(_this,i,start,end);};})(i,start,end),0));}} +return _results;};Renderer.prototype.executeFilter=function(){Event.trigger(this.c,"processStart",this.currentJob);if(this.currentJob.type===Filter.Type.Single){return this.eachBlock(this.renderBlock);}else{return this.eachBlock(this.renderKernel);}};Renderer.prototype.executePlugin=function(){Log.debug("Executing plugin "+this.currentJob.plugin);Plugin.execute(this.c,this.currentJob.plugin,this.currentJob.args);Log.debug("Plugin "+this.currentJob.plugin+" finished!");return this.processNext();};Renderer.prototype.renderBlock=function(bnum,start,end){var i,pixel,_i;Log.debug("Block #"+bnum+" - Filter: "+this.currentJob.name+", Start: "+start+", End: "+end);Event.trigger(this.c,"blockStarted",{blockNum:bnum,totalBlocks:Renderer.Blocks,startPixel:start,endPixel:end});pixel=new Pixel();pixel.setContext(this.c);for(i=_i=start;_i=builder;j=-builder<=builder?++_j:--_j){for(k=_k=builder;builder<=-builder?_k<=-builder:_k>=-builder;k=builder<=-builder?++_k:--_k){p=pixel.getPixelRelative(j,k);kernel[builderIndex*3]=p.r;kernel[builderIndex*3+1]=p.g;kernel[builderIndex*3+2]=p.b;builderIndex++;}} +res=this.processKernel(adjust,kernel,divisor,bias);this.modPixelData[i]=Util.clampRGB(res.r);this.modPixelData[i+1]=Util.clampRGB(res.g);this.modPixelData[i+2]=Util.clampRGB(res.b);this.modPixelData[i+3]=this.c.pixelData[i+3];} +if(Caman.NodeJS){return Fiber["yield"](bnum);}else{return this.blockFinished(bnum);}};Renderer.prototype.blockFinished=function(bnum){var i,_i,_ref;if(bnum>=0){Log.debug("Block #"+bnum+" finished! Filter: "+this.currentJob.name);} +this.blocksDone++;Event.trigger(this.c,"blockFinished",{blockNum:bnum,blocksFinished:this.blocksDone,totalBlocks:Renderer.Blocks});if(this.blocksDone===Renderer.Blocks){if(this.currentJob.type===Filter.Type.Kernel){for(i=_i=0,_ref=this.c.pixelData.length;0<=_ref?_i<_ref:_i>_ref;i=0<=_ref?++_i:--_i){this.c.pixelData[i]=this.modPixelData[i];}} +if(bnum>=0){Log.debug("Filter "+this.currentJob.name+" finished!");} +Event.trigger(this.c,"processComplete",this.currentJob);return this.processNext();}};Renderer.prototype.processKernel=function(adjust,kernel,divisor,bias){var i,val,_i,_ref;val={r:0,g:0,b:0};for(i=_i=0,_ref=adjust.length;0<=_ref?_i<_ref:_i>_ref;i=0<=_ref?++_i:--_i){val.r+=adjust[i]*kernel[i*3];val.g+=adjust[i]*kernel[i*3+1];val.b+=adjust[i]*kernel[i*3+2];} +val.r=(val.r/divisor)+bias;val.g=(val.g/divisor)+bias;val.b=(val.b/divisor)+bias;return val;};Renderer.prototype.loadOverlay=function(layer,src){var img,proxyUrl,_this=this;img=new Image();img.onload=function(){layer.context.drawImage(img,0,0,_this.c.dimensions.width,_this.c.dimensions.height);layer.imageData=layer.context.getImageData(0,0,_this.c.dimensions.width,_this.c.dimensions.height);layer.pixelData=layer.imageData.data;_this.c.pixelData=layer.pixelData;return _this.processNext();};proxyUrl=IO.remoteCheck(src);return img.src=proxyUrl!=null?proxyUrl:src;};return Renderer;})();Renderer=Caman.Renderer;Caman.Store=(function(){function Store(){} +Store.items={};Store.has=function(search){return this.items[search]!=null;};Store.get=function(search){return this.items[search];};Store.put=function(name,obj){return this.items[name]=obj;};Store.execute=function(search,callback){var _this=this;setTimeout(function(){return callback.call(_this.get(search),_this.get(search));},0);return this.get(search);};Store.flush=function(name){if(name==null){name=false;} +if(name){return delete this.items[name];}else{return this.items={};}};return Store;})();Store=Caman.Store;Blender.register("normal",function(rgbaLayer,rgbaParent){return{r:rgbaLayer.r,g:rgbaLayer.g,b:rgbaLayer.b};});Blender.register("multiply",function(rgbaLayer,rgbaParent){return{r:(rgbaLayer.r*rgbaParent.r)/255,g:(rgbaLayer.g*rgbaParent.g)/255,b:(rgbaLayer.b*rgbaParent.b)/255};});Blender.register("screen",function(rgbaLayer,rgbaParent){return{r:255-(((255-rgbaLayer.r)*(255-rgbaParent.r))/255),g:255-(((255-rgbaLayer.g)*(255-rgbaParent.g))/255),b:255-(((255-rgbaLayer.b)*(255-rgbaParent.b))/255)};});Blender.register("overlay",function(rgbaLayer,rgbaParent){var result;result={};result.r=rgbaParent.r>128?255-2*(255-rgbaLayer.r)*(255-rgbaParent.r)/255:(rgbaParent.r*rgbaLayer.r*2)/255;result.g=rgbaParent.g>128?255-2*(255-rgbaLayer.g)*(255-rgbaParent.g)/255:(rgbaParent.g*rgbaLayer.g*2)/255;result.b=rgbaParent.b>128?255-2*(255-rgbaLayer.b)*(255-rgbaParent.b)/255:(rgbaParent.b*rgbaLayer.b*2)/255;return result;});Blender.register("difference",function(rgbaLayer,rgbaParent){return{r:rgbaLayer.r-rgbaParent.r,g:rgbaLayer.g-rgbaParent.g,b:rgbaLayer.b-rgbaParent.b};});Blender.register("addition",function(rgbaLayer,rgbaParent){return{r:rgbaParent.r+rgbaLayer.r,g:rgbaParent.g+rgbaLayer.g,b:rgbaParent.b+rgbaLayer.b};});Blender.register("exclusion",function(rgbaLayer,rgbaParent){return{r:128-2*(rgbaParent.r-128)*(rgbaLayer.r-128)/255,g:128-2*(rgbaParent.g-128)*(rgbaLayer.g-128)/255,b:128-2*(rgbaParent.b-128)*(rgbaLayer.b-128)/255};});Blender.register("softLight",function(rgbaLayer,rgbaParent){var result;result={};result.r=rgbaParent.r>128?255-((255-rgbaParent.r)*(255-(rgbaLayer.r-128)))/255:(rgbaParent.r*(rgbaLayer.r+128))/255;result.g=rgbaParent.g>128?255-((255-rgbaParent.g)*(255-(rgbaLayer.g-128)))/255:(rgbaParent.g*(rgbaLayer.g+128))/255;result.b=rgbaParent.b>128?255-((255-rgbaParent.b)*(255-(rgbaLayer.b-128)))/255:(rgbaParent.b*(rgbaLayer.b+128))/255;return result;});Blender.register("lighten",function(rgbaLayer,rgbaParent){return{r:rgbaParent.r>rgbaLayer.r?rgbaParent.r:rgbaLayer.r,g:rgbaParent.g>rgbaLayer.g?rgbaParent.g:rgbaLayer.g,b:rgbaParent.b>rgbaLayer.b?rgbaParent.b:rgbaLayer.b};});Blender.register("darken",function(rgbaLayer,rgbaParent){return{r:rgbaParent.r>rgbaLayer.r?rgbaLayer.r:rgbaParent.r,g:rgbaParent.g>rgbaLayer.g?rgbaLayer.g:rgbaParent.g,b:rgbaParent.b>rgbaLayer.b?rgbaLayer.b:rgbaParent.b};});Filter.register("fillColor",function(){var color;if(arguments.length===1){color=Convert.hexToRGB(arguments[0]);}else{color={r:arguments[0],g:arguments[1],b:arguments[2]};} +return this.process("fillColor",function(rgba){rgba.r=color.r;rgba.g=color.g;rgba.b=color.b;rgba.a=255;return rgba;});});Filter.register("brightness",function(adjust){adjust=Math.floor(255*(adjust/100));return this.process("brightness",function(rgba){rgba.r+=adjust;rgba.g+=adjust;rgba.b+=adjust;return rgba;});});Filter.register("saturation",function(adjust){adjust*=-0.01;return this.process("saturation",function(rgba){var max;max=Math.max(rgba.r,rgba.g,rgba.b);if(rgba.r!==max){rgba.r+=(max-rgba.r)*adjust;} +if(rgba.g!==max){rgba.g+=(max-rgba.g)*adjust;} +if(rgba.b!==max){rgba.b+=(max-rgba.b)*adjust;} +return rgba;});});Filter.register("vibrance",function(adjust){adjust*=-1;return this.process("vibrance",function(rgba){var amt,avg,max;max=Math.max(rgba.r,rgba.g,rgba.b);avg=(rgba.r+rgba.g+rgba.b)/3;amt=((Math.abs(max-avg)*2/255)*adjust)/100;if(rgba.r!==max){rgba.r+=(max-rgba.r)*amt;} +if(rgba.g!==max){rgba.g+=(max-rgba.g)*amt;} +if(rgba.b!==max){rgba.b+=(max-rgba.b)*amt;} +return rgba;});});Filter.register("greyscale",function(adjust){return this.process("greyscale",function(rgba){var avg;avg=Calculate.luminance(rgba);rgba.r=avg;rgba.g=avg;rgba.b=avg;return rgba;});});Filter.register("contrast",function(adjust){adjust=Math.pow((adjust+100)/100,2);return this.process("contrast",function(rgba){rgba.r/=255;rgba.r-=0.5;rgba.r*=adjust;rgba.r+=0.5;rgba.r*=255;rgba.g/=255;rgba.g-=0.5;rgba.g*=adjust;rgba.g+=0.5;rgba.g*=255;rgba.b/=255;rgba.b-=0.5;rgba.b*=adjust;rgba.b+=0.5;rgba.b*=255;return rgba;});});Filter.register("hue",function(adjust){return this.process("hue",function(rgba){var b,g,h,hsv,r,_ref;hsv=Convert.rgbToHSV(rgba.r,rgba.g,rgba.b);h=hsv.h*100;h+=Math.abs(adjust);h=h%100;h/=100;hsv.h=h;_ref=Convert.hsvToRGB(hsv.h,hsv.s,hsv.v),r=_ref.r,g=_ref.g,b=_ref.b;rgba.r=r;rgba.g=g;rgba.b=b;return rgba;});});Filter.register("colorize",function(){var level,rgb;if(arguments.length===2){rgb=Convert.hexToRGB(arguments[0]);level=arguments[1];}else if(arguments.length===4){rgb={r:arguments[0],g:arguments[1],b:arguments[2]};level=arguments[3];} +return this.process("colorize",function(rgba){rgba.r-=(rgba.r-rgb.r)*(level/100);rgba.g-=(rgba.g-rgb.g)*(level/100);rgba.b-=(rgba.b-rgb.b)*(level/100);return rgba;});});Filter.register("invert",function(){return this.process("invert",function(rgba){rgba.r=255-rgba.r;rgba.g=255-rgba.g;rgba.b=255-rgba.b;return rgba;});});Filter.register("sepia",function(adjust){if(adjust==null){adjust=100;} +adjust/=100;return this.process("sepia",function(rgba){rgba.r=Math.min(255,(rgba.r*(1-(0.607*adjust)))+(rgba.g*(0.769*adjust))+(rgba.b*(0.189*adjust)));rgba.g=Math.min(255,(rgba.r*(0.349*adjust))+(rgba.g*(1-(0.314*adjust)))+(rgba.b*(0.168*adjust)));rgba.b=Math.min(255,(rgba.r*(0.272*adjust))+(rgba.g*(0.534*adjust))+(rgba.b*(1-(0.869*adjust))));return rgba;});});Filter.register("gamma",function(adjust){return this.process("gamma",function(rgba){rgba.r=Math.pow(rgba.r/255,adjust)*255;rgba.g=Math.pow(rgba.g/255,adjust)*255;rgba.b=Math.pow(rgba.b/255,adjust)*255;return rgba;});});Filter.register("noise",function(adjust){adjust=Math.abs(adjust)*2.55;return this.process("noise",function(rgba){var rand;rand=Calculate.randomRange(adjust*-1,adjust);rgba.r+=rand;rgba.g+=rand;rgba.b+=rand;return rgba;});});Filter.register("clip",function(adjust){adjust=Math.abs(adjust)*2.55;return this.process("clip",function(rgba){if(rgba.r>255-adjust){rgba.r=255;}else if(rgba.r255-adjust){rgba.g=255;}else if(rgba.g255-adjust){rgba.b=255;}else if(rgba.b0){rgba.r+=(255-rgba.r)*options.red;}else{rgba.r-=rgba.r*Math.abs(options.red);}} +if(options.green!=null){if(options.green>0){rgba.g+=(255-rgba.g)*options.green;}else{rgba.g-=rgba.g*Math.abs(options.green);}} +if(options.blue!=null){if(options.blue>0){rgba.b+=(255-rgba.b)*options.blue;}else{rgba.b-=rgba.b*Math.abs(options.blue);}} +return rgba;});});Filter.register("curves",function(){var algo,bezier,chans,cps,end,i,last,start,_i,_j,_ref,_ref1;chans=arguments[0],cps=2<=arguments.length?__slice.call(arguments,1):[];last=cps[cps.length-1];if(typeof last==="function"){algo=last;cps.pop();}else if(typeof last==="string"){algo=Calculate[last];cps.pop();}else{algo=Calculate.bezier;} +if(typeof chans==="string"){chans=chans.split("");} +if(chans[0]==="v"){chans=['r','g','b'];} +if(cps.length<2){throw"Invalid number of arguments to curves filter";} +bezier=algo(cps,0,255);start=cps[0];if(start[0]>0){for(i=_i=0,_ref=start[0];0<=_ref?_i<_ref:_i>_ref;i=0<=_ref?++_i:--_i){bezier[i]=start[1];}} +end=cps[cps.length-1];if(end[0]<255){for(i=_j=_ref1=end[0];_ref1<=255?_j<=255:_j>=255;i=_ref1<=255?++_j:--_j){bezier[i]=end[1];}} +return this.process("curves",function(rgba){var _k,_ref2;for(i=_k=0,_ref2=chans.length;0<=_ref2?_k<_ref2:_k>_ref2;i=0<=_ref2?++_k:--_k){rgba[chans[i]]=bezier[rgba[chans[i]]];} +return rgba;});});Filter.register("exposure",function(adjust){var ctrl1,ctrl2,p;p=Math.abs(adjust)/100;ctrl1=[0,255*p];ctrl2=[255-(255*p),255];if(adjust<0){ctrl1=ctrl1.reverse();ctrl2=ctrl2.reverse();} +return this.curves('rgb',[0,0],ctrl1,ctrl2,[255,255]);});Caman.Plugin.register("crop",function(width,height,x,y){var canvas,ctx;if(x==null){x=0;} +if(y==null){y=0;} +if(typeof exports!=="undefined"&&exports!==null){canvas=new Canvas(width,height);}else{canvas=document.createElement('canvas');Util.copyAttributes(this.canvas,canvas);canvas.width=width;canvas.height=height;} +ctx=canvas.getContext('2d');ctx.drawImage(this.canvas,x,y,width,height,0,0,width,height);this.cropCoordinates={x:x,y:y};this.cropped=true;return this.replaceCanvas(canvas);});Caman.Plugin.register("resize",function(newDims){var canvas,ctx;if(newDims==null){newDims=null;} +if(newDims===null||((newDims.width==null)&&(newDims.height==null))){Log.error("Invalid or missing dimensions given for resize");return;} +if(newDims.width==null){newDims.width=this.canvas.width*newDims.height/this.canvas.height;}else if(newDims.height==null){newDims.height=this.canvas.height*newDims.width/this.canvas.width;} +if(typeof exports!=="undefined"&&exports!==null){canvas=new Canvas(newDims.width,newDims.height);}else{canvas=document.createElement('canvas');Util.copyAttributes(this.canvas,canvas);canvas.width=newDims.width;canvas.height=newDims.height;} +ctx=canvas.getContext('2d');ctx.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,newDims.width,newDims.height);this.resized=true;return this.replaceCanvas(canvas);});Caman.Filter.register("crop",function(){return this.processPlugin("crop",Array.prototype.slice.call(arguments,0));});Caman.Filter.register("resize",function(){return this.processPlugin("resize",Array.prototype.slice.call(arguments,0));});Caman.Filter.register("boxBlur",function(){return this.processKernel("Box Blur",[1,1,1,1,1,1,1,1,1]);});Caman.Filter.register("heavyRadialBlur",function(){return this.processKernel("Heavy Radial Blur",[0,0,1,0,0,0,1,1,1,0,1,1,1,1,1,0,1,1,1,0,0,0,1,0,0]);});Caman.Filter.register("gaussianBlur",function(){return this.processKernel("Gaussian Blur",[1,4,6,4,1,4,16,24,16,4,6,24,36,24,6,4,16,24,16,4,1,4,6,4,1]);});Caman.Filter.register("motionBlur",function(degrees){var kernel;if(degrees===0||degrees===180){kernel=[0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0];}else if((degrees>0&°rees<90)||(degrees>180&°rees<270)){kernel=[0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0];}else if(degrees===90||degrees===270){kernel=[0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0];}else{kernel=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1];} +return this.processKernel("Motion Blur",kernel);});Caman.Filter.register("sharpen",function(amt){if(amt==null){amt=100;} +amt/=100;return this.processKernel("Sharpen",[0,-amt,0,-amt,4*amt+1,-amt,0,-amt,0]);});vignetteFilters={brightness:function(rgba,amt,opts){rgba.r=rgba.r-(rgba.r*amt*opts.strength);rgba.g=rgba.g-(rgba.g*amt*opts.strength);rgba.b=rgba.b-(rgba.b*amt*opts.strength);return rgba;},gamma:function(rgba,amt,opts){rgba.r=Math.pow(rgba.r/255,Math.max(10*amt*opts.strength,1))*255;rgba.g=Math.pow(rgba.g/255,Math.max(10*amt*opts.strength,1))*255;rgba.b=Math.pow(rgba.b/255,Math.max(10*amt*opts.strength,1))*255;return rgba;},colorize:function(rgba,amt,opts){rgba.r-=(rgba.r-opts.color.r)*amt;rgba.g-=(rgba.g-opts.color.g)*amt;rgba.b-=(rgba.b-opts.color.b)*amt;return rgba;}};Filter.register("vignette",function(size,strength){var bezier,center,end,start;if(strength==null){strength=60;} +if(typeof size==="string"&&size.substr(-1)==="%"){if(this.dimensions.height>this.dimensions.width){size=this.dimensions.width*(parseInt(size.substr(0,size.length-1),10)/100);}else{size=this.dimensions.height*(parseInt(size.substr(0,size.length-1),10)/100);}} +strength/=100;center=[this.dimensions.width/2,this.dimensions.height/2];start=Math.sqrt(Math.pow(center[0],2)+Math.pow(center[1],2));end=start-size;bezier=Calculate.bezier([0,1],[30,30],[70,60],[100,80]);return this.process("vignette",function(rgba){var dist,div,loc;loc=rgba.locationXY();dist=Calculate.distance(loc.x,loc.y,center[0],center[1]);if(dist>end){div=Math.max(1,(bezier[Math.round(((dist-end)/size)*100)]/10)*strength);rgba.r=Math.pow(rgba.r/255,div)*255;rgba.g=Math.pow(rgba.g/255,div)*255;rgba.b=Math.pow(rgba.b/255,div)*255;} +return rgba;});});Filter.register("rectangularVignette",function(opts){var defaults,dim,percent,size,_i,_len,_ref;defaults={strength:50,cornerRadius:0,method:'brightness',color:{r:0,g:0,b:0}};opts=Util.extend(defaults,opts);if(!opts.size){return this;}else if(typeof opts.size==="string"){percent=parseInt(opts.size,10)/100;opts.size={width:this.dimensions.width*percent,height:this.dimensions.height*percent};}else if(typeof opts.size==="object"){_ref=["width","height"];for(_i=0,_len=_ref.length;_i<_len;_i++){dim=_ref[_i];if(typeof opts.size[dim]==="string"){opts.size[dim]=this.dimensions[dim]*(parseInt(opts.size[dim],10)/100);}}}else if(opts.size==="number"){size=opts.size;opts.size={width:size,height:size};} +if(typeof opts.cornerRadius==="string"){opts.cornerRadius=(opts.size.width/2)*(parseInt(opts.cornerRadius,10)/100);} +opts.strength/=100;opts.size.width=Math.floor(opts.size.width);opts.size.height=Math.floor(opts.size.height);opts.image={width:this.dimensions.width,height:this.dimensions.height};if(opts.method==="colorize"&&typeof opts.color==="string"){opts.color=Convert.hexToRGB(opts.color);} +opts.coords={left:(this.dimensions.width-opts.size.width)/2,right:this.dimensions.width-opts.coords.left,bottom:(this.dimensions.height-opts.size.height)/2,top:this.dimensions.height-opts.coords.bottom};opts.corners=[{x:opts.coords.left+opts.cornerRadius,y:opts.coords.top-opts.cornerRadius},{x:opts.coords.right-opts.cornerRadius,y:opts.coords.top-opts.cornerRadius},{x:opts.coords.right-opts.cornerRadius,y:opts.coords.bottom+opts.cornerRadius},{x:opts.coords.left+opts.cornerRadius,y:opts.coords.bottom+opts.cornerRadius}];opts.maxDist=Calculate.distance(0,0,opts.corners[3].x,opts.corners[3].y)-opts.cornerRadius;return this.process("rectangularVignette",function(rgba){var amt,loc,radialDist;loc=rgba.locationXY();if((loc.x>opts.corners[0].x&&loc.xopts.coords.bottom&&loc.yopts.coords.left&&loc.xopts.corners[3].y&&loc.yopts.corners[0].x&&loc.xopts.coords.top){amt=(loc.y-opts.coords.top)/opts.maxDist;}else if(loc.y>opts.corners[2].y&&loc.yopts.coords.right){amt=(loc.x-opts.coords.right)/opts.maxDist;}else if(loc.x>opts.corners[0].x&&loc.xopts.corners[2].y&&loc.y=opts.corners[0].y){radialDist=Caman.distance(loc.x,loc.y,opts.corners[0].x,opts.corners[0].y);amt=(radialDist-opts.cornerRadius)/opts.maxDist;}else if(loc.x>=opts.corners[1].x&&loc.y>=opts.corners[1].y){radialDist=Caman.distance(loc.x,loc.y,opts.corners[1].x,opts.corners[1].y);amt=(radialDist-opts.cornerRadius)/opts.maxDist;}else if(loc.x>=opts.corners[2].x&&loc.y<=opts.corners[2].y){radialDist=Caman.distance(loc.x,loc.y,opts.corners[2].x,opts.corners[2].y);amt=(radialDist-opts.cornerRadius)/opts.maxDist;}else if(loc.x<=opts.corners[3].x&&loc.y<=opts.corners[3].y){radialDist=Caman.distance(loc.x,loc.y,opts.corners[3].x,opts.corners[3].y);amt=(radialDist-opts.cornerRadius)/opts.maxDist;} +if(amt<0){return rgba;} +return vignetteFilters[opts.method](rgba,amt,opts);});});(function(){var BlurStack,getLinearGradientMap,getRadialGradientMap,mul_table,shg_table;mul_table=[512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259];shg_table=[9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24];getLinearGradientMap=function(width,height,centerX,centerY,angle,length,mirrored){var cnv,context,gradient,x1,x2,y1,y2;cnv=typeof exports!=="undefined"&&exports!==null?new Canvas():document.createElement('canvas');cnv.width=width;cnv.height=height;x1=centerX+Math.cos(angle)*length*0.5;y1=centerY+Math.sin(angle)*length*0.5;x2=centerX-Math.cos(angle)*length*0.5;y2=centerY-Math.sin(angle)*length*0.5;context=cnv.getContext("2d");gradient=context.createLinearGradient(x1,y1,x2,y2);if(!mirrored){gradient.addColorStop(0,"white");gradient.addColorStop(1,"black");}else{gradient.addColorStop(0,"white");gradient.addColorStop(0.5,"black");gradient.addColorStop(1,"white");} +context.fillStyle=gradient;context.fillRect(0,0,width,height);return context.getImageData(0,0,width,height);};getRadialGradientMap=function(width,height,centerX,centerY,radius1,radius2){var cnv,context,gradient;cnv=typeof exports!=="undefined"&&exports!==null?new Canvas():document.createElement('canvas');cnv.width=width;cnv.height=height;context=cnv.getContext("2d");gradient=context.createRadialGradient(centerX,centerY,radius1,centerX,centerY,radius2);gradient.addColorStop(1,"white");gradient.addColorStop(0,"black");context.fillStyle=gradient;context.fillRect(0,0,width,height);return context.getImageData(0,0,width,height);};BlurStack=function(){this.r=0;this.g=0;this.b=0;this.a=0;return this.next=null;};Caman.Plugin.register("compoundBlur",function(radiusData,radius,increaseFactor,blurLevels){var b_in_sum,b_out_sum,b_sum,blend,currentIndex,div,g_in_sum,g_out_sum,g_sum,height,heightMinus1,i,iblend,idx,imagePixels,index,iradius,lookupValue,mul_sum,p,pb,pg,pixels,pr,r_in_sum,r_out_sum,r_sum,radiusPixels,radiusPlus1,rbs,shg_sum,stack,stackEnd,stackIn,stackOut,stackStart,steps,sumFactor,w4,wh,wh4,width,widthMinus1,x,y,yi,yp,yw,_i,_j,_k,_l,_m,_n,_o,_p,_q,_r;width=this.dimensions.width;height=this.dimensions.height;imagePixels=this.pixelData;radiusPixels=radiusData.data;wh=width*height;wh4=wh<<2;pixels=[];for(i=_i=0;0<=wh4?_iwh4;i=0<=wh4?++_i:--_i){pixels[i]=imagePixels[i];} +currentIndex=0;steps=blurLevels;blurLevels-=1;while(steps-->=0){iradius=(radius+0.5)|0;if(iradius===0){continue;} +if(iradius>256){iradius=256;} +div=iradius+iradius+1;w4=width<<2;widthMinus1=width-1;heightMinus1=height-1;radiusPlus1=iradius+1;sumFactor=radiusPlus1*(radiusPlus1+1)/2;stackStart=new BlurStack();stackEnd=void 0;stack=stackStart;for(i=_j=1;1<=div?_jdiv;i=1<=div?++_j:--_j){stack=stack.next=new BlurStack();if(i===radiusPlus1){stackEnd=stack;}} +stack.next=stackStart;stackIn=null;stackOut=null;yw=yi=0;mul_sum=mul_table[iradius];shg_sum=shg_table[iradius];for(y=_k=0;0<=height?_kheight;y=0<=height?++_k:--_k){r_in_sum=g_in_sum=b_in_sum=r_sum=g_sum=b_sum=0;r_out_sum=radiusPlus1*(pr=pixels[yi]);g_out_sum=radiusPlus1*(pg=pixels[yi+1]);b_out_sum=radiusPlus1*(pb=pixels[yi+2]);r_sum+=sumFactor*pr;g_sum+=sumFactor*pg;b_sum+=sumFactor*pb;stack=stackStart;for(i=_l=0;0<=radiusPlus1?_lradiusPlus1;i=0<=radiusPlus1?++_l:--_l){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next;} +for(i=_m=1;1<=radiusPlus1?_mradiusPlus1;i=1<=radiusPlus1?++_m:--_m){p=yi+((widthMinus1width;x=0<=width?++_n:--_n){pixels[yi]=(r_sum*mul_sum)>>shg_sum;pixels[yi+1]=(g_sum*mul_sum)>>shg_sum;pixels[yi+2]=(b_sum*mul_sum)>>shg_sum;r_sum-=r_out_sum;g_sum-=g_out_sum;b_sum-=b_out_sum;r_out_sum-=stackIn.r;g_out_sum-=stackIn.g;b_out_sum-=stackIn.b;p=(yw+((p=x+radiusPlus1)width;x=0<=width?++_o:--_o){g_in_sum=b_in_sum=r_in_sum=g_sum=b_sum=r_sum=0;yi=x<<2;r_out_sum=radiusPlus1*(pr=pixels[yi]);g_out_sum=radiusPlus1*(pg=pixels[yi+1]);b_out_sum=radiusPlus1*(pb=pixels[yi+2]);r_sum+=sumFactor*pr;g_sum+=sumFactor*pg;b_sum+=sumFactor*pb;stack=stackStart;for(i=_p=0;0<=radiusPlus1?_pradiusPlus1;i=0<=radiusPlus1?++_p:--_p){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next;} +yp=width;for(i=_q=1;1<=radiusPlus1?_qradiusPlus1;i=1<=radiusPlus1?++_q:--_q){yi=(yp+x)<<2;r_sum+=(stack.r=(pr=pixels[yi]))*(rbs=radiusPlus1-i);g_sum+=(stack.g=(pg=pixels[yi+1]))*rbs;b_sum+=(stack.b=(pb=pixels[yi+2]))*rbs;r_in_sum+=pr;g_in_sum+=pg;b_in_sum+=pb;stack=stack.next;if(iheight;y=0<=height?++_r:--_r){p=yi<<2;pixels[p]=(r_sum*mul_sum)>>shg_sum;pixels[p+1]=(g_sum*mul_sum)>>shg_sum;pixels[p+2]=(b_sum*mul_sum)>>shg_sum;r_sum-=r_out_sum;g_sum-=g_out_sum;b_sum-=b_out_sum;r_out_sum-=stackIn.r;g_out_sum-=stackIn.g;b_out_sum-=stackIn.b;p=(x+(((p=y+radiusPlus1)-1){idx=i<<2;lookupValue=(radiusPixels[idx+2]&0xff)/255.0*blurLevels;index=lookupValue|0;if(index===currentIndex){blend=256.0*(lookupValue-(lookupValue|0));iblend=256-blend;imagePixels[idx]=(imagePixels[idx]*iblend+pixels[idx]*blend)>>8;imagePixels[idx+1]=(imagePixels[idx+1]*iblend+pixels[idx+1]*blend)>>8;imagePixels[idx+2]=(imagePixels[idx+2]*iblend+pixels[idx+2]*blend)>>8;}else if(index===currentIndex+1){imagePixels[idx]=pixels[idx];imagePixels[idx+1]=pixels[idx+1];imagePixels[idx+2]=pixels[idx+2];}} +currentIndex++;} +return this;});Caman.Filter.register("tiltShift",function(opts){var defaults,gradient;defaults={center:{x:this.dimensions.width/2,y:this.dimensions.height/2},angle:45,focusWidth:200,startRadius:3,radiusFactor:1.5,steps:3};opts=Util.extend(defaults,opts);opts.angle*=Math.PI/180;gradient=getLinearGradientMap(this.dimensions.width,this.dimensions.height,opts.center.x,opts.center.y,opts.angle,opts.focusWidth,true);return this.processPlugin("compoundBlur",[gradient,opts.startRadius,opts.radiusFactor,opts.steps]);});return Caman.Filter.register("radialBlur",function(opts){var defaults,gradient,radius1,radius2;defaults={size:50,center:{x:this.dimensions.width/2,y:this.dimensions.height/2},startRadius:3,radiusFactor:1.5,steps:3,radius:null};opts=Util.extend(defaults,opts);if(!opts.radius){opts.radius=this.dimensions.widthdiv;i=1<=div?++_i:--_i){stack=stack.next=new BlurStack();if(i===radiusPlus1){stackEnd=stack;}} +stack.next=stackStart;stackIn=null;stackOut=null;yw=yi=0;mul_sum=mul_table[radius];shg_sum=shg_table[radius];for(y=_j=0;0<=height?_jheight;y=0<=height?++_j:--_j){r_in_sum=g_in_sum=b_in_sum=r_sum=g_sum=b_sum=0;r_out_sum=radiusPlus1*(pr=pixels[yi]);g_out_sum=radiusPlus1*(pg=pixels[yi+1]);b_out_sum=radiusPlus1*(pb=pixels[yi+2]);r_sum+=sumFactor*pr;g_sum+=sumFactor*pg;b_sum+=sumFactor*pb;stack=stackStart;for(i=_k=0;0<=radiusPlus1?_kradiusPlus1;i=0<=radiusPlus1?++_k:--_k){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next;} +for(i=_l=1;1<=radiusPlus1?_lradiusPlus1;i=1<=radiusPlus1?++_l:--_l){p=yi+((widthMinus1width;x=0<=width?++_m:--_m){pixels[yi]=(r_sum*mul_sum)>>shg_sum;pixels[yi+1]=(g_sum*mul_sum)>>shg_sum;pixels[yi+2]=(b_sum*mul_sum)>>shg_sum;r_sum-=r_out_sum;g_sum-=g_out_sum;b_sum-=b_out_sum;r_out_sum-=stackIn.r;g_out_sum-=stackIn.g;b_out_sum-=stackIn.b;p=(yw+((p=x+radius+1)width;x=0<=width?++_n:--_n){g_in_sum=b_in_sum=r_in_sum=g_sum=b_sum=r_sum=0;yi=x<<2;r_out_sum=radiusPlus1*(pr=pixels[yi]);g_out_sum=radiusPlus1*(pg=pixels[yi+1]);b_out_sum=radiusPlus1*(pb=pixels[yi+2]);r_sum+=sumFactor*pr;g_sum+=sumFactor*pg;b_sum+=sumFactor*pb;stack=stackStart;for(i=_o=0;0<=radiusPlus1?_oradiusPlus1;i=0<=radiusPlus1?++_o:--_o){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next;} +yp=width;for(i=_p=1;1<=radius?_p<=radius:_p>=radius;i=1<=radius?++_p:--_p){yi=(yp+x)<<2;r_sum+=(stack.r=(pr=pixels[yi]))*(rbs=radiusPlus1-i);g_sum+=(stack.g=(pg=pixels[yi+1]))*rbs;b_sum+=(stack.b=(pb=pixels[yi+2]))*rbs;r_in_sum+=pr;g_in_sum+=pg;b_in_sum+=pb;stack=stack.next;if(iheight;y=0<=height?++_q:--_q){p=yi<<2;pixels[p]=(r_sum*mul_sum)>>shg_sum;pixels[p+1]=(g_sum*mul_sum)>>shg_sum;pixels[p+2]=(b_sum*mul_sum)>>shg_sum;r_sum-=r_out_sum;g_sum-=g_out_sum;b_sum-=b_out_sum;r_out_sum-=stackIn.r;g_out_sum-=stackIn.g;b_out_sum-=stackIn.b;p=(x+(((p=y+radiusPlus1) + + + + + + + + + _Cacheable__manager_id + http_cache + + + __name__ + caman.full.min.js + + + content_type + text/javascript + + + precondition + + + + title + caman.full.min.js + + + + + diff --git a/bt5/erp5_document_scanner/bt/template_action_path_list b/bt5/erp5_document_scanner/bt/template_action_path_list index 1febd2b17a8..6a5c64c903c 100644 --- a/bt5/erp5_document_scanner/bt/template_action_path_list +++ b/bt5/erp5_document_scanner/bt/template_action_path_list @@ -1,5 +1,6 @@ Accounting Transaction | scan_document Internal Invoice Transaction | scan_document Payment Transaction | scan_document +Preference | document_scanner_preference Purchase Invoice Transaction | scan_document Sale Invoice Transaction | scan_document \ No newline at end of file diff --git a/bt5/erp5_document_scanner/bt/template_property_sheet_id_list b/bt5/erp5_document_scanner/bt/template_property_sheet_id_list new file mode 100644 index 00000000000..1158a56c5ae --- /dev/null +++ b/bt5/erp5_document_scanner/bt/template_property_sheet_id_list @@ -0,0 +1 @@ +DocumentScannerPreference \ No newline at end of file -- 2.30.9 From 6af494ea4d9e613ee78ecf2e6356c7e211d0b85d Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Wed, 11 Dec 2019 22:01:05 +0000 Subject: [PATCH 40/42] erp5_document_scanner: Rename field to be more consistent --- .../Preference_viewDocumentScannerSettting.xml | 8 ++++---- ... my_preferred_image_scanner_conversion_brightness.xml} | 2 +- ...my_preferred_image_scanner_conversion_compression.xml} | 2 +- ...=> my_preferred_image_scanner_conversion_contrast.xml} | 2 +- ...eferred_image_scanner_conversion_enable_greyscale.xml} | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) rename bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/{my_preferred_image_conversion_brightness.xml => my_preferred_image_scanner_conversion_brightness.xml} (97%) rename bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/{my_preferred_image_conversion_compression.xml => my_preferred_image_scanner_conversion_compression.xml} (97%) rename bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/{my_preferred_image_conversion_contrast.xml => my_preferred_image_scanner_conversion_contrast.xml} (97%) rename bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/{my_preferred_image_conversion_enable_greyscale.xml => my_preferred_image_scanner_conversion_enable_greyscale.xml} (96%) diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting.xml index 59763369779..b75d59c6756 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting.xml @@ -83,7 +83,7 @@ left - my_preferred_image_conversion_enable_greyscale + my_preferred_image_scanner_conversion_enable_greyscale
@@ -91,9 +91,9 @@ right - my_preferred_image_conversion_compression - my_preferred_image_conversion_brightness - my_preferred_image_conversion_contrast + my_preferred_image_scanner_conversion_compression + my_preferred_image_scanner_conversion_brightness + my_preferred_image_scanner_conversion_contrast
diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_brightness.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_brightness.xml similarity index 97% rename from bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_brightness.xml rename to bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_brightness.xml index a400a0fa445..c8e0cdd5fa5 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_brightness.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_brightness.xml @@ -16,7 +16,7 @@
id - my_preferred_image_conversion_brightness + my_preferred_image_scanner_conversion_brightness message_values diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_compression.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_compression.xml similarity index 97% rename from bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_compression.xml rename to bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_compression.xml index 0ae8fbda3dc..ace59bfd9c9 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_compression.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_compression.xml @@ -16,7 +16,7 @@ id - my_preferred_image_conversion_compression + my_preferred_image_scanner_conversion_compression message_values diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_contrast.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_contrast.xml similarity index 97% rename from bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_contrast.xml rename to bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_contrast.xml index e8d841d4876..8a2bb4e03f0 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_contrast.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_contrast.xml @@ -16,7 +16,7 @@ id - my_preferred_image_conversion_contrast + my_preferred_image_scanner_conversion_contrast message_values diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_enable_greyscale.xml b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_enable_greyscale.xml similarity index 96% rename from bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_enable_greyscale.xml rename to bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_enable_greyscale.xml index 3ba56efd049..fdab7fdb4d3 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_conversion_enable_greyscale.xml +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Preference_viewDocumentScannerSettting/my_preferred_image_scanner_conversion_enable_greyscale.xml @@ -16,7 +16,7 @@ id - my_preferred_image_conversion_enable_greyscale + my_preferred_image_scanner_conversion_enable_greyscale message_values -- 2.30.9 From 332347c635cbb630d0a518d95dae3492313f0ce1 Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Wed, 11 Dec 2019 22:01:51 +0000 Subject: [PATCH 41/42] erp5_document_scanner_ui_test: Add test to scan document preference --- .../testScanDocumentPreference.xml | 58 +++++++++++++ .../testScanDocumentPreference.zpt | 85 +++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocumentPreference.xml create mode 100644 bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocumentPreference.zpt diff --git a/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocumentPreference.xml b/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocumentPreference.xml new file mode 100644 index 00000000000..b8b8f3db045 --- /dev/null +++ b/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocumentPreference.xml @@ -0,0 +1,58 @@ + + + + + + + + + + _bind_names + + + + + + + + + + _asgns + + + + name_subpath + traverse_subpath + + + + + + + + + + + content_type + text/html + + + expand + 0 + + + id + testScanDocumentPreference + + + output_encoding + utf-8 + + + title + Test Scan Document Preference + + + + + diff --git a/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocumentPreference.zpt b/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocumentPreference.zpt new file mode 100644 index 00000000000..58597ecc820 --- /dev/null +++ b/bt5/erp5_document_scanner_ui_test/PathTemplateItem/portal_tests/renderjs_erp5_document_scanner_zuite/testScanDocumentPreference.zpt @@ -0,0 +1,85 @@ + + + +Test Scan Document (expected failure) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Test Scan Document (expected failure)
open${base_url}/Zuite_waitForActivities
assertTextPresentDone.
open${base_url}/web_site_module/renderjs_runner/
waitForTextPresentPreferences
click//a[text()="Preferences"]
waitForElementPresent//dd/a[text()="Document Scanner"]
click//dd/a[text()="Document Scanner"]
waitForTextPresentDocument Scanner
waitForElementPresent//input[@id="field_my_preferred_image_scanner_conversion_brightness"]
click//input[@id="field_my_preferred_image_scanner_conversion_enable_greyscale"]
type//input[@id="field_my_preferred_image_scanner_conversion_brightness"]20
type//input[@id="field_my_preferred_image_scanner_conversion_contrast"]20
click//button[@name="submit"]
waitForElementPresent//div[@class="visible"]/button[text()="Data updated."]
+ + \ No newline at end of file -- 2.30.9 From 263e3a3b339bcda3caffe11623d298818cd2d9cb Mon Sep 17 00:00:00 2001 From: Gabriel Monnerat Date: Thu, 12 Dec 2019 01:01:57 +0000 Subject: [PATCH 42/42] erp5_document_scanner: Rename property to be more consistent --- ...rred_image_scanner_conversion_brightness_property.xml} | 2 +- ...red_image_scanner_conversion_compression_property.xml} | 2 +- ...ferred_image_scanner_conversion_contrast_property.xml} | 2 +- ...mage_scanner_conversion_enable_greyscale_property.xml} | 2 +- .../Base_getPreferredImageSettingsFromPreference.py | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) rename bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/{preferred_image_conversion_brightness_property.xml => preferred_image_scanner_conversion_brightness_property.xml} (94%) rename bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/{preferred_image_conversion_compression_property.xml => preferred_image_scanner_conversion_compression_property.xml} (94%) rename bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/{preferred_image_conversion_contrast_property.xml => preferred_image_scanner_conversion_contrast_property.xml} (94%) rename bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/{preferred_image_conversion_enable_greyscale_property.xml => preferred_image_scanner_conversion_enable_greyscale_property.xml} (94%) diff --git a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_brightness_property.xml b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_brightness_property.xml similarity index 94% rename from bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_brightness_property.xml rename to bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_brightness_property.xml index fe136f1be20..981bc70917c 100644 --- a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_brightness_property.xml +++ b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_brightness_property.xml @@ -39,7 +39,7 @@
id - preferred_image_conversion_brightness_property + preferred_image_scanner_conversion_brightness_property mode diff --git a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_compression_property.xml b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_compression_property.xml similarity index 94% rename from bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_compression_property.xml rename to bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_compression_property.xml index a376ede9fb4..fb8a1a0eaff 100644 --- a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_compression_property.xml +++ b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_compression_property.xml @@ -39,7 +39,7 @@ id - preferred_image_conversion_compression_property + preferred_image_scanner_conversion_compression_property mode diff --git a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_contrast_property.xml b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_contrast_property.xml similarity index 94% rename from bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_contrast_property.xml rename to bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_contrast_property.xml index 495989518a0..df0ebc4d797 100644 --- a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_contrast_property.xml +++ b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_contrast_property.xml @@ -39,7 +39,7 @@ id - preferred_image_conversion_contrast_property + preferred_image_scanner_conversion_contrast_property mode diff --git a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_enable_greyscale_property.xml b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_enable_greyscale_property.xml similarity index 94% rename from bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_enable_greyscale_property.xml rename to bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_enable_greyscale_property.xml index 69e199ca1ef..55f4127aa4d 100644 --- a/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_conversion_enable_greyscale_property.xml +++ b/bt5/erp5_document_scanner/PropertySheetTemplateItem/portal_property_sheets/DocumentScannerPreference/preferred_image_scanner_conversion_enable_greyscale_property.xml @@ -39,7 +39,7 @@ id - preferred_image_conversion_enable_greyscale_property + preferred_image_scanner_conversion_enable_greyscale_property mode diff --git a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.py b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.py index c03c04f59a4..3fead8983ab 100644 --- a/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.py +++ b/bt5/erp5_document_scanner/SkinTemplateItem/portal_skins/erp5_document_scanner/Base_getPreferredImageSettingsFromPreference.py @@ -6,10 +6,10 @@ if not active_preference: active_preference = portal.portal_preferences.getActivePreference() setting_dict = { - "compression": active_preference.getPreferredImageConversionCompression(), - "enable_greyscale": active_preference.getPreferredImageConversionEnableGreyscale(), - "brightness": active_preference.getPreferredImageConversionBrightness(), - "contrast": active_preference.getPreferredImageConversionContrast(), + "compression": active_preference.getPreferredImageScannerConversionCompression(), + "enable_greyscale": active_preference.getPreferredImageScannerConversionEnableGreyscale(), + "brightness": active_preference.getPreferredImageScannerConversionBrightness(), + "contrast": active_preference.getPreferredImageScannerConversionContrast(), } return json.dumps(setting_dict) -- 2.30.9