Jordi Ballester Alomar
4 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 4896 additions and 0 deletions
-
2pos_jsprintmanager/__init__.py
-
22pos_jsprintmanager/__manifest__.py
-
2pos_jsprintmanager/controllers/__init__.py
-
26pos_jsprintmanager/controllers/main.py
-
1pos_jsprintmanager/models/__init__.py
-
18pos_jsprintmanager/models/pos_config.py
-
11pos_jsprintmanager/readme/CONFIGURE.rst
-
6pos_jsprintmanager/readme/CONTRIBUTORS.rst
-
11pos_jsprintmanager/readme/DESCRIPTION.rst
-
3pos_jsprintmanager/readme/USAGE.rst
-
1184pos_jsprintmanager/static/src/js/jsprintmanager/JSPrintManager.js
-
31pos_jsprintmanager/static/src/js/jsprintmanager/bluebird.min.js
-
2060pos_jsprintmanager/static/src/js/jsprintmanager/deflate.js
-
20pos_jsprintmanager/static/src/js/jsprintmanager/html2canvas.min.js
-
259pos_jsprintmanager/static/src/js/jsprintmanager/zip-ext.js
-
966pos_jsprintmanager/static/src/js/jsprintmanager/zip.js
-
213pos_jsprintmanager/static/src/js/screen.js
-
20pos_jsprintmanager/views/assets.xml
-
41pos_jsprintmanager/views/pos_config_view.xml
@ -0,0 +1,2 @@ |
|||||
|
from . import models |
||||
|
from . import controllers |
@ -0,0 +1,22 @@ |
|||||
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). |
||||
|
|
||||
|
{ |
||||
|
"name": "Pos JS Print Manager", |
||||
|
"category": "Point Of Sale", |
||||
|
"version": "12.0.1.0.0", |
||||
|
"author": "ForgeFlow, " |
||||
|
"Odoo Community Association (OCA)", |
||||
|
"website": "https://github.com/OCA/pos", |
||||
|
"license": "LGPL-3", |
||||
|
"depends": [ |
||||
|
"point_of_sale", |
||||
|
], |
||||
|
"data": [ |
||||
|
"views/assets.xml", |
||||
|
"views/pos_config_view.xml", |
||||
|
], |
||||
|
"qweb": [ |
||||
|
'static/src/xml/pos.xml', |
||||
|
], |
||||
|
"installable": True, |
||||
|
} |
@ -0,0 +1,2 @@ |
|||||
|
|
||||
|
from . import main |
@ -0,0 +1,26 @@ |
|||||
|
# Copyright 2020 ForgeFlow, S.L. |
||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). |
||||
|
|
||||
|
import logging |
||||
|
import uuid |
||||
|
from hashlib import sha256 |
||||
|
|
||||
|
from odoo import http |
||||
|
from odoo.http import request |
||||
|
|
||||
|
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
|
||||
|
class JsPrintManagerController(http.Controller): |
||||
|
|
||||
|
@http.route('/jspm', type='http', auth="none") |
||||
|
def get_jspm_license(self, **kw): |
||||
|
""" Route called when google sends a Accept/Refuse auth """ |
||||
|
icp = request.env['ir.config_parameter'].sudo() |
||||
|
license_owner = icp.get_param('jsprintmanager.license_owner') |
||||
|
license_key = icp.get_param('jsprintmanager.license_key') |
||||
|
uid = str(uuid.uuid1()) |
||||
|
shasign = sha256((license_key + uid).encode('utf-8')) |
||||
|
license_hash = shasign.hexdigest() |
||||
|
output = "|".join([license_owner, license_hash, uid]) |
||||
|
return request.make_response(output) |
@ -0,0 +1 @@ |
|||||
|
from . import pos_config |
@ -0,0 +1,18 @@ |
|||||
|
|
||||
|
from odoo import models, fields |
||||
|
|
||||
|
|
||||
|
class PosConfig(models.Model): |
||||
|
_inherit = 'pos.config' |
||||
|
|
||||
|
use_jsprintmanager = fields.Boolean() |
||||
|
jsprintmanager_default_receipt_printer = fields.Char( |
||||
|
string='Default Printer for Receipts', |
||||
|
help='Enter the name of the default printer to be used in receipts') |
||||
|
jsprintmanager_output_format = fields.Selection( |
||||
|
string='Printer output format', |
||||
|
selection=[ |
||||
|
('normal', 'Normal'), |
||||
|
('escpos', 'ESC/POS') |
||||
|
], |
||||
|
help='Enter the format used by the printer') |
@ -0,0 +1,11 @@ |
|||||
|
To add a logo to any given company: |
||||
|
|
||||
|
#. Go to *Settings > Users & Companies > Companies* |
||||
|
#. Edit one and add the logo editing the top left corner image. |
||||
|
|
||||
|
To configure receipt web print in the PoS (is the default setting): |
||||
|
|
||||
|
#. Go to *Point of Sale > Configuration > Point of Sale*. |
||||
|
#. Edit the one you want to configure. |
||||
|
#. If the *PosBox* setting is enabled the *Receipt Printer* setting should be |
||||
|
disabled. |
@ -0,0 +1,6 @@ |
|||||
|
* Endika Iglesias <endikaig@antiun.com> |
||||
|
|
||||
|
* `Tecnativa <https://www.tecnativa.com>`_: |
||||
|
|
||||
|
* Antonio Espinosa |
||||
|
* David Vidal |
@ -0,0 +1,11 @@ |
|||||
|
A different receipt template is used if the PoS ticket is printed via web or |
||||
|
via proxy. In the case the ticket is printed via web (through the browser) the |
||||
|
company logo isn't printed. This module adds it. |
||||
|
|
||||
|
In other hand, company_logo is loaded using `/web/binary/company_logo` |
||||
|
controller `that returns a 150px wide logo <https://github.com/odoo/odoo/blob/11.0/addons/point_of_sale/static/src/js/models.js#L481>`_: |
||||
|
|
||||
|
but after that logo is resized to 300px width, so a pixelled logo appears even |
||||
|
original logo is 300px wide. |
||||
|
That's why we override how company_logo is loaded. We also resized it to 260px |
||||
|
(not 300px) wide because appears cut in PDF. |
@ -0,0 +1,3 @@ |
|||||
|
#. Open a new PoS session. |
||||
|
#. Make an order and validate it. |
||||
|
#. You should see the company logo in the receipt preview. |
1184
pos_jsprintmanager/static/src/js/jsprintmanager/JSPrintManager.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
31
pos_jsprintmanager/static/src/js/jsprintmanager/bluebird.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2060
pos_jsprintmanager/static/src/js/jsprintmanager/deflate.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
20
pos_jsprintmanager/static/src/js/jsprintmanager/html2canvas.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,259 @@ |
|||||
|
/* |
||||
|
Copyright (c) 2013 Gildas Lormeau. All rights reserved. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are met: |
||||
|
|
||||
|
1. Redistributions of source code must retain the above copyright notice, |
||||
|
this list of conditions and the following disclaimer. |
||||
|
|
||||
|
2. Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the distribution. |
||||
|
|
||||
|
3. The names of the authors may not be used to endorse or promote products |
||||
|
derived from this software without specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, |
||||
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
||||
|
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, |
||||
|
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, |
||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
||||
|
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
*/ |
||||
|
|
||||
|
(function() { |
||||
|
"use strict"; |
||||
|
|
||||
|
var ERR_HTTP_RANGE = "HTTP Range not supported."; |
||||
|
|
||||
|
var Reader = zip.Reader; |
||||
|
var Writer = zip.Writer; |
||||
|
|
||||
|
var ZipDirectoryEntry; |
||||
|
|
||||
|
var appendABViewSupported; |
||||
|
try { |
||||
|
appendABViewSupported = new Blob([ new DataView(new ArrayBuffer(0)) ]).size === 0; |
||||
|
} catch (e) { |
||||
|
} |
||||
|
|
||||
|
function isHttpFamily(url) { |
||||
|
var a = document.createElement("a"); |
||||
|
a.href = url; |
||||
|
return a.protocol === "http:" || a.protocol === "https:"; |
||||
|
} |
||||
|
|
||||
|
function HttpReader(url) { |
||||
|
var that = this; |
||||
|
|
||||
|
function getData(callback, onerror) { |
||||
|
var request; |
||||
|
if (!that.data) { |
||||
|
request = new XMLHttpRequest(); |
||||
|
request.addEventListener("load", function() { |
||||
|
if (!that.size) |
||||
|
that.size = Number(request.getResponseHeader("Content-Length")) || Number(request.response.byteLength); |
||||
|
that.data = new Uint8Array(request.response); |
||||
|
callback(); |
||||
|
}, false); |
||||
|
request.addEventListener("error", onerror, false); |
||||
|
request.open("GET", url); |
||||
|
request.responseType = "arraybuffer"; |
||||
|
request.send(); |
||||
|
} else |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function init(callback, onerror) { |
||||
|
if (!isHttpFamily(url)) { |
||||
|
// For schemas other than http(s), HTTP HEAD may be unavailable,
|
||||
|
// so use HTTP GET instead.
|
||||
|
getData(callback, onerror); |
||||
|
return; |
||||
|
} |
||||
|
var request = new XMLHttpRequest(); |
||||
|
request.addEventListener("load", function() { |
||||
|
that.size = Number(request.getResponseHeader("Content-Length")); |
||||
|
// If response header doesn't return size then prefetch the content.
|
||||
|
if (!that.size) { |
||||
|
getData(callback, onerror); |
||||
|
} else { |
||||
|
callback(); |
||||
|
} |
||||
|
}, false); |
||||
|
request.addEventListener("error", onerror, false); |
||||
|
request.open("HEAD", url); |
||||
|
request.send(); |
||||
|
} |
||||
|
|
||||
|
function readUint8Array(index, length, callback, onerror) { |
||||
|
getData(function() { |
||||
|
callback(new Uint8Array(that.data.subarray(index, index + length))); |
||||
|
}, onerror); |
||||
|
} |
||||
|
|
||||
|
that.size = 0; |
||||
|
that.init = init; |
||||
|
that.readUint8Array = readUint8Array; |
||||
|
} |
||||
|
HttpReader.prototype = new Reader(); |
||||
|
HttpReader.prototype.constructor = HttpReader; |
||||
|
|
||||
|
function HttpRangeReader(url) { |
||||
|
var that = this; |
||||
|
|
||||
|
function init(callback, onerror) { |
||||
|
var request = new XMLHttpRequest(); |
||||
|
request.addEventListener("load", function() { |
||||
|
that.size = Number(request.getResponseHeader("Content-Length")); |
||||
|
if (request.getResponseHeader("Accept-Ranges") == "bytes") |
||||
|
callback(); |
||||
|
else |
||||
|
onerror(ERR_HTTP_RANGE); |
||||
|
}, false); |
||||
|
request.addEventListener("error", onerror, false); |
||||
|
request.open("HEAD", url); |
||||
|
request.send(); |
||||
|
} |
||||
|
|
||||
|
function readArrayBuffer(index, length, callback, onerror) { |
||||
|
var request = new XMLHttpRequest(); |
||||
|
request.open("GET", url); |
||||
|
request.responseType = "arraybuffer"; |
||||
|
request.setRequestHeader("Range", "bytes=" + index + "-" + (index + length - 1)); |
||||
|
request.addEventListener("load", function() { |
||||
|
callback(request.response); |
||||
|
}, false); |
||||
|
request.addEventListener("error", onerror, false); |
||||
|
request.send(); |
||||
|
} |
||||
|
|
||||
|
function readUint8Array(index, length, callback, onerror) { |
||||
|
readArrayBuffer(index, length, function(arraybuffer) { |
||||
|
callback(new Uint8Array(arraybuffer)); |
||||
|
}, onerror); |
||||
|
} |
||||
|
|
||||
|
that.size = 0; |
||||
|
that.init = init; |
||||
|
that.readUint8Array = readUint8Array; |
||||
|
} |
||||
|
HttpRangeReader.prototype = new Reader(); |
||||
|
HttpRangeReader.prototype.constructor = HttpRangeReader; |
||||
|
|
||||
|
function ArrayBufferReader(arrayBuffer) { |
||||
|
var that = this; |
||||
|
|
||||
|
function init(callback, onerror) { |
||||
|
that.size = arrayBuffer.byteLength; |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function readUint8Array(index, length, callback, onerror) { |
||||
|
callback(new Uint8Array(arrayBuffer.slice(index, index + length))); |
||||
|
} |
||||
|
|
||||
|
that.size = 0; |
||||
|
that.init = init; |
||||
|
that.readUint8Array = readUint8Array; |
||||
|
} |
||||
|
ArrayBufferReader.prototype = new Reader(); |
||||
|
ArrayBufferReader.prototype.constructor = ArrayBufferReader; |
||||
|
|
||||
|
function ArrayBufferWriter() { |
||||
|
var array, that = this; |
||||
|
|
||||
|
function init(callback, onerror) { |
||||
|
array = new Uint8Array(); |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function writeUint8Array(arr, callback, onerror) { |
||||
|
var tmpArray = new Uint8Array(array.length + arr.length); |
||||
|
tmpArray.set(array); |
||||
|
tmpArray.set(arr, array.length); |
||||
|
array = tmpArray; |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function getData(callback) { |
||||
|
callback(array.buffer); |
||||
|
} |
||||
|
|
||||
|
that.init = init; |
||||
|
that.writeUint8Array = writeUint8Array; |
||||
|
that.getData = getData; |
||||
|
} |
||||
|
ArrayBufferWriter.prototype = new Writer(); |
||||
|
ArrayBufferWriter.prototype.constructor = ArrayBufferWriter; |
||||
|
|
||||
|
function FileWriter(fileEntry, contentType) { |
||||
|
var writer, that = this; |
||||
|
|
||||
|
function init(callback, onerror) { |
||||
|
fileEntry.createWriter(function(fileWriter) { |
||||
|
writer = fileWriter; |
||||
|
callback(); |
||||
|
}, onerror); |
||||
|
} |
||||
|
|
||||
|
function writeUint8Array(array, callback, onerror) { |
||||
|
var blob = new Blob([ appendABViewSupported ? array : array.buffer ], { |
||||
|
type : contentType |
||||
|
}); |
||||
|
writer.onwrite = function() { |
||||
|
writer.onwrite = null; |
||||
|
callback(); |
||||
|
}; |
||||
|
writer.onerror = onerror; |
||||
|
writer.write(blob); |
||||
|
} |
||||
|
|
||||
|
function getData(callback) { |
||||
|
fileEntry.file(callback); |
||||
|
} |
||||
|
|
||||
|
that.init = init; |
||||
|
that.writeUint8Array = writeUint8Array; |
||||
|
that.getData = getData; |
||||
|
} |
||||
|
FileWriter.prototype = new Writer(); |
||||
|
FileWriter.prototype.constructor = FileWriter; |
||||
|
|
||||
|
zip.FileWriter = FileWriter; |
||||
|
zip.HttpReader = HttpReader; |
||||
|
zip.HttpRangeReader = HttpRangeReader; |
||||
|
zip.ArrayBufferReader = ArrayBufferReader; |
||||
|
zip.ArrayBufferWriter = ArrayBufferWriter; |
||||
|
|
||||
|
if (zip.fs) { |
||||
|
ZipDirectoryEntry = zip.fs.ZipDirectoryEntry; |
||||
|
ZipDirectoryEntry.prototype.addHttpContent = function(name, URL, useRangeHeader) { |
||||
|
function addChild(parent, name, params, directory) { |
||||
|
if (parent.directory) |
||||
|
return directory ? new ZipDirectoryEntry(parent.fs, name, params, parent) : new zip.fs.ZipFileEntry(parent.fs, name, params, parent); |
||||
|
else |
||||
|
throw "Parent entry is not a directory."; |
||||
|
} |
||||
|
|
||||
|
return addChild(this, name, { |
||||
|
data : URL, |
||||
|
Reader : useRangeHeader ? HttpRangeReader : HttpReader |
||||
|
}); |
||||
|
}; |
||||
|
ZipDirectoryEntry.prototype.importHttpContent = function(URL, useRangeHeader, onend, onerror) { |
||||
|
this.importZip(useRangeHeader ? new HttpRangeReader(URL) : new HttpReader(URL), onend, onerror); |
||||
|
}; |
||||
|
zip.fs.FS.prototype.importHttpContent = function(URL, useRangeHeader, onend, onerror) { |
||||
|
this.entries = []; |
||||
|
this.root = new ZipDirectoryEntry(this); |
||||
|
this.root.importHttpContent(URL, useRangeHeader, onend, onerror); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
})(); |
@ -0,0 +1,966 @@ |
|||||
|
/* |
||||
|
Copyright (c) 2013 Gildas Lormeau. All rights reserved. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are met: |
||||
|
|
||||
|
1. Redistributions of source code must retain the above copyright notice, |
||||
|
this list of conditions and the following disclaimer. |
||||
|
|
||||
|
2. Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the distribution. |
||||
|
|
||||
|
3. The names of the authors may not be used to endorse or promote products |
||||
|
derived from this software without specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, |
||||
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
||||
|
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, |
||||
|
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, |
||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
||||
|
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
*/ |
||||
|
|
||||
|
(function(obj) { |
||||
|
"use strict"; |
||||
|
|
||||
|
var ERR_BAD_FORMAT = "File format is not recognized."; |
||||
|
var ERR_CRC = "CRC failed."; |
||||
|
var ERR_ENCRYPTED = "File contains encrypted entry."; |
||||
|
var ERR_ZIP64 = "File is using Zip64 (4gb+ file size)."; |
||||
|
var ERR_READ = "Error while reading zip file."; |
||||
|
var ERR_WRITE = "Error while writing zip file."; |
||||
|
var ERR_WRITE_DATA = "Error while writing file data."; |
||||
|
var ERR_READ_DATA = "Error while reading file data."; |
||||
|
var ERR_DUPLICATED_NAME = "File already exists."; |
||||
|
var CHUNK_SIZE = 512 * 1024; |
||||
|
|
||||
|
var TEXT_PLAIN = "text/plain"; |
||||
|
|
||||
|
var appendABViewSupported; |
||||
|
try { |
||||
|
appendABViewSupported = new Blob([ new DataView(new ArrayBuffer(0)) ]).size === 0; |
||||
|
} catch (e) { |
||||
|
} |
||||
|
|
||||
|
function Crc32() { |
||||
|
this.crc = -1; |
||||
|
} |
||||
|
Crc32.prototype.append = function append(data) { |
||||
|
var crc = this.crc | 0, table = this.table; |
||||
|
for (var offset = 0, len = data.length | 0; offset < len; offset++) |
||||
|
crc = (crc >>> 8) ^ table[(crc ^ data[offset]) & 0xFF]; |
||||
|
this.crc = crc; |
||||
|
}; |
||||
|
Crc32.prototype.get = function get() { |
||||
|
return ~this.crc; |
||||
|
}; |
||||
|
Crc32.prototype.table = (function() { |
||||
|
var i, j, t, table = []; // Uint32Array is actually slower than []
|
||||
|
for (i = 0; i < 256; i++) { |
||||
|
t = i; |
||||
|
for (j = 0; j < 8; j++) |
||||
|
if (t & 1) |
||||
|
t = (t >>> 1) ^ 0xEDB88320; |
||||
|
else |
||||
|
t = t >>> 1; |
||||
|
table[i] = t; |
||||
|
} |
||||
|
return table; |
||||
|
})(); |
||||
|
|
||||
|
// "no-op" codec
|
||||
|
function NOOP() {} |
||||
|
NOOP.prototype.append = function append(bytes, onprogress) { |
||||
|
return bytes; |
||||
|
}; |
||||
|
NOOP.prototype.flush = function flush() {}; |
||||
|
|
||||
|
function blobSlice(blob, index, length) { |
||||
|
if (index < 0 || length < 0 || index + length > blob.size) |
||||
|
throw new RangeError('offset:' + index + ', length:' + length + ', size:' + blob.size); |
||||
|
if (blob.slice) |
||||
|
return blob.slice(index, index + length); |
||||
|
else if (blob.webkitSlice) |
||||
|
return blob.webkitSlice(index, index + length); |
||||
|
else if (blob.mozSlice) |
||||
|
return blob.mozSlice(index, index + length); |
||||
|
else if (blob.msSlice) |
||||
|
return blob.msSlice(index, index + length); |
||||
|
} |
||||
|
|
||||
|
function getDataHelper(byteLength, bytes) { |
||||
|
var dataBuffer, dataArray; |
||||
|
dataBuffer = new ArrayBuffer(byteLength); |
||||
|
dataArray = new Uint8Array(dataBuffer); |
||||
|
if (bytes) |
||||
|
dataArray.set(bytes, 0); |
||||
|
return { |
||||
|
buffer : dataBuffer, |
||||
|
array : dataArray, |
||||
|
view : new DataView(dataBuffer) |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
// Readers
|
||||
|
function Reader() { |
||||
|
} |
||||
|
|
||||
|
function TextReader(text) { |
||||
|
var that = this, blobReader; |
||||
|
|
||||
|
function init(callback, onerror) { |
||||
|
var blob = new Blob([ text ], { |
||||
|
type : TEXT_PLAIN |
||||
|
}); |
||||
|
blobReader = new BlobReader(blob); |
||||
|
blobReader.init(function() { |
||||
|
that.size = blobReader.size; |
||||
|
callback(); |
||||
|
}, onerror); |
||||
|
} |
||||
|
|
||||
|
function readUint8Array(index, length, callback, onerror) { |
||||
|
blobReader.readUint8Array(index, length, callback, onerror); |
||||
|
} |
||||
|
|
||||
|
that.size = 0; |
||||
|
that.init = init; |
||||
|
that.readUint8Array = readUint8Array; |
||||
|
} |
||||
|
TextReader.prototype = new Reader(); |
||||
|
TextReader.prototype.constructor = TextReader; |
||||
|
|
||||
|
function Data64URIReader(dataURI) { |
||||
|
var that = this, dataStart; |
||||
|
|
||||
|
function init(callback) { |
||||
|
var dataEnd = dataURI.length; |
||||
|
while (dataURI.charAt(dataEnd - 1) == "=") |
||||
|
dataEnd--; |
||||
|
dataStart = dataURI.indexOf(",") + 1; |
||||
|
that.size = Math.floor((dataEnd - dataStart) * 0.75); |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function readUint8Array(index, length, callback) { |
||||
|
var i, data = getDataHelper(length); |
||||
|
var start = Math.floor(index / 3) * 4; |
||||
|
var end = Math.ceil((index + length) / 3) * 4; |
||||
|
var bytes = obj.atob(dataURI.substring(start + dataStart, end + dataStart)); |
||||
|
var delta = index - Math.floor(start / 4) * 3; |
||||
|
for (i = delta; i < delta + length; i++) |
||||
|
data.array[i - delta] = bytes.charCodeAt(i); |
||||
|
callback(data.array); |
||||
|
} |
||||
|
|
||||
|
that.size = 0; |
||||
|
that.init = init; |
||||
|
that.readUint8Array = readUint8Array; |
||||
|
} |
||||
|
Data64URIReader.prototype = new Reader(); |
||||
|
Data64URIReader.prototype.constructor = Data64URIReader; |
||||
|
|
||||
|
function BlobReader(blob) { |
||||
|
var that = this; |
||||
|
|
||||
|
function init(callback) { |
||||
|
that.size = blob.size; |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function readUint8Array(index, length, callback, onerror) { |
||||
|
var reader = new FileReader(); |
||||
|
reader.onload = function(e) { |
||||
|
callback(new Uint8Array(e.target.result)); |
||||
|
}; |
||||
|
reader.onerror = onerror; |
||||
|
try { |
||||
|
reader.readAsArrayBuffer(blobSlice(blob, index, length)); |
||||
|
} catch (e) { |
||||
|
onerror(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
that.size = 0; |
||||
|
that.init = init; |
||||
|
that.readUint8Array = readUint8Array; |
||||
|
} |
||||
|
BlobReader.prototype = new Reader(); |
||||
|
BlobReader.prototype.constructor = BlobReader; |
||||
|
|
||||
|
// Writers
|
||||
|
|
||||
|
function Writer() { |
||||
|
} |
||||
|
Writer.prototype.getData = function(callback) { |
||||
|
callback(this.data); |
||||
|
}; |
||||
|
|
||||
|
function TextWriter(encoding) { |
||||
|
var that = this, blob; |
||||
|
|
||||
|
function init(callback) { |
||||
|
blob = new Blob([], { |
||||
|
type : TEXT_PLAIN |
||||
|
}); |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function writeUint8Array(array, callback) { |
||||
|
blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], { |
||||
|
type : TEXT_PLAIN |
||||
|
}); |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function getData(callback, onerror) { |
||||
|
var reader = new FileReader(); |
||||
|
reader.onload = function(e) { |
||||
|
callback(e.target.result); |
||||
|
}; |
||||
|
reader.onerror = onerror; |
||||
|
reader.readAsText(blob, encoding); |
||||
|
} |
||||
|
|
||||
|
that.init = init; |
||||
|
that.writeUint8Array = writeUint8Array; |
||||
|
that.getData = getData; |
||||
|
} |
||||
|
TextWriter.prototype = new Writer(); |
||||
|
TextWriter.prototype.constructor = TextWriter; |
||||
|
|
||||
|
function Data64URIWriter(contentType) { |
||||
|
var that = this, data = "", pending = ""; |
||||
|
|
||||
|
function init(callback) { |
||||
|
data += "data:" + (contentType || "") + ";base64,"; |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function writeUint8Array(array, callback) { |
||||
|
var i, delta = pending.length, dataString = pending; |
||||
|
pending = ""; |
||||
|
for (i = 0; i < (Math.floor((delta + array.length) / 3) * 3) - delta; i++) |
||||
|
dataString += String.fromCharCode(array[i]); |
||||
|
for (; i < array.length; i++) |
||||
|
pending += String.fromCharCode(array[i]); |
||||
|
if (dataString.length > 2) |
||||
|
data += obj.btoa(dataString); |
||||
|
else |
||||
|
pending = dataString; |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function getData(callback) { |
||||
|
callback(data + obj.btoa(pending)); |
||||
|
} |
||||
|
|
||||
|
that.init = init; |
||||
|
that.writeUint8Array = writeUint8Array; |
||||
|
that.getData = getData; |
||||
|
} |
||||
|
Data64URIWriter.prototype = new Writer(); |
||||
|
Data64URIWriter.prototype.constructor = Data64URIWriter; |
||||
|
|
||||
|
function BlobWriter(contentType) { |
||||
|
var blob, that = this; |
||||
|
|
||||
|
function init(callback) { |
||||
|
blob = new Blob([], { |
||||
|
type : contentType |
||||
|
}); |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function writeUint8Array(array, callback) { |
||||
|
blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], { |
||||
|
type : contentType |
||||
|
}); |
||||
|
callback(); |
||||
|
} |
||||
|
|
||||
|
function getData(callback) { |
||||
|
callback(blob); |
||||
|
} |
||||
|
|
||||
|
that.init = init; |
||||
|
that.writeUint8Array = writeUint8Array; |
||||
|
that.getData = getData; |
||||
|
} |
||||
|
BlobWriter.prototype = new Writer(); |
||||
|
BlobWriter.prototype.constructor = BlobWriter; |
||||
|
|
||||
|
/** |
||||
|
* inflate/deflate core functions |
||||
|
* @param worker {Worker} web worker for the task. |
||||
|
* @param initialMessage {Object} initial message to be sent to the worker. should contain |
||||
|
* sn(serial number for distinguishing multiple tasks sent to the worker), and codecClass. |
||||
|
* This function may add more properties before sending. |
||||
|
*/ |
||||
|
function launchWorkerProcess(worker, initialMessage, reader, writer, offset, size, onprogress, onend, onreaderror, onwriteerror) { |
||||
|
var chunkIndex = 0, index, outputSize, sn = initialMessage.sn, crc; |
||||
|
|
||||
|
function onflush() { |
||||
|
worker.removeEventListener('message', onmessage, false); |
||||
|
onend(outputSize, crc); |
||||
|
} |
||||
|
|
||||
|
function onmessage(event) { |
||||
|
var message = event.data, data = message.data, err = message.error; |
||||
|
if (err) { |
||||
|
err.toString = function () { return 'Error: ' + this.message; }; |
||||
|
onreaderror(err); |
||||
|
return; |
||||
|
} |
||||
|
if (message.sn !== sn) |
||||
|
return; |
||||
|
if (typeof message.codecTime === 'number') |
||||
|
worker.codecTime += message.codecTime; // should be before onflush()
|
||||
|
if (typeof message.crcTime === 'number') |
||||
|
worker.crcTime += message.crcTime; |
||||
|
|
||||
|
switch (message.type) { |
||||
|
case 'append': |
||||
|
if (data) { |
||||
|
outputSize += data.length; |
||||
|
writer.writeUint8Array(data, function() { |
||||
|
step(); |
||||
|
}, onwriteerror); |
||||
|
} else |
||||
|
step(); |
||||
|
break; |
||||
|
case 'flush': |
||||
|
crc = message.crc; |
||||
|
if (data) { |
||||
|
outputSize += data.length; |
||||
|
writer.writeUint8Array(data, function() { |
||||
|
onflush(); |
||||
|
}, onwriteerror); |
||||
|
} else |
||||
|
onflush(); |
||||
|
break; |
||||
|
case 'progress': |
||||
|
if (onprogress) |
||||
|
onprogress(index + message.loaded, size); |
||||
|
break; |
||||
|
case 'importScripts': //no need to handle here
|
||||
|
case 'newTask': |
||||
|
case 'echo': |
||||
|
break; |
||||
|
default: |
||||
|
console.warn('zip.js:launchWorkerProcess: unknown message: ', message); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function step() { |
||||
|
index = chunkIndex * CHUNK_SIZE; |
||||
|
// use `<=` instead of `<`, because `size` may be 0.
|
||||
|
if (index <= size) { |
||||
|
reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function(array) { |
||||
|
if (onprogress) |
||||
|
onprogress(index, size); |
||||
|
var msg = index === 0 ? initialMessage : {sn : sn}; |
||||
|
msg.type = 'append'; |
||||
|
msg.data = array; |
||||
|
|
||||
|
// posting a message with transferables will fail on IE10
|
||||
|
try { |
||||
|
worker.postMessage(msg, [array.buffer]); |
||||
|
} catch(ex) { |
||||
|
worker.postMessage(msg); // retry without transferables
|
||||
|
} |
||||
|
chunkIndex++; |
||||
|
}, onreaderror); |
||||
|
} else { |
||||
|
worker.postMessage({ |
||||
|
sn: sn, |
||||
|
type: 'flush' |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
outputSize = 0; |
||||
|
worker.addEventListener('message', onmessage, false); |
||||
|
step(); |
||||
|
} |
||||
|
|
||||
|
function launchProcess(process, reader, writer, offset, size, crcType, onprogress, onend, onreaderror, onwriteerror) { |
||||
|
var chunkIndex = 0, index, outputSize = 0, |
||||
|
crcInput = crcType === 'input', |
||||
|
crcOutput = crcType === 'output', |
||||
|
crc = new Crc32(); |
||||
|
function step() { |
||||
|
var outputData; |
||||
|
index = chunkIndex * CHUNK_SIZE; |
||||
|
if (index < size) |
||||
|
reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function(inputData) { |
||||
|
var outputData; |
||||
|
try { |
||||
|
outputData = process.append(inputData, function(loaded) { |
||||
|
if (onprogress) |
||||
|
onprogress(index + loaded, size); |
||||
|
}); |
||||
|
} catch (e) { |
||||
|
onreaderror(e); |
||||
|
return; |
||||
|
} |
||||
|
if (outputData) { |
||||
|
outputSize += outputData.length; |
||||
|
writer.writeUint8Array(outputData, function() { |
||||
|
chunkIndex++; |
||||
|
setTimeout(step, 1); |
||||
|
}, onwriteerror); |
||||
|
if (crcOutput) |
||||
|
crc.append(outputData); |
||||
|
} else { |
||||
|
chunkIndex++; |
||||
|
setTimeout(step, 1); |
||||
|
} |
||||
|
if (crcInput) |
||||
|
crc.append(inputData); |
||||
|
if (onprogress) |
||||
|
onprogress(index, size); |
||||
|
}, onreaderror); |
||||
|
else { |
||||
|
try { |
||||
|
outputData = process.flush(); |
||||
|
} catch (e) { |
||||
|
onreaderror(e); |
||||
|
return; |
||||
|
} |
||||
|
if (outputData) { |
||||
|
if (crcOutput) |
||||
|
crc.append(outputData); |
||||
|
outputSize += outputData.length; |
||||
|
writer.writeUint8Array(outputData, function() { |
||||
|
onend(outputSize, crc.get()); |
||||
|
}, onwriteerror); |
||||
|
} else |
||||
|
onend(outputSize, crc.get()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
step(); |
||||
|
} |
||||
|
|
||||
|
function inflate(worker, sn, reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) { |
||||
|
var crcType = computeCrc32 ? 'output' : 'none'; |
||||
|
if (obj.zip.useWebWorkers) { |
||||
|
var initialMessage = { |
||||
|
sn: sn, |
||||
|
codecClass: 'Inflater', |
||||
|
crcType: crcType, |
||||
|
}; |
||||
|
launchWorkerProcess(worker, initialMessage, reader, writer, offset, size, onprogress, onend, onreaderror, onwriteerror); |
||||
|
} else |
||||
|
launchProcess(new obj.zip.Inflater(), reader, writer, offset, size, crcType, onprogress, onend, onreaderror, onwriteerror); |
||||
|
} |
||||
|
|
||||
|
function deflate(worker, sn, reader, writer, level, onend, onprogress, onreaderror, onwriteerror) { |
||||
|
var crcType = 'input'; |
||||
|
if (obj.zip.useWebWorkers) { |
||||
|
var initialMessage = { |
||||
|
sn: sn, |
||||
|
options: {level: level}, |
||||
|
codecClass: 'Deflater', |
||||
|
crcType: crcType, |
||||
|
}; |
||||
|
launchWorkerProcess(worker, initialMessage, reader, writer, 0, reader.size, onprogress, onend, onreaderror, onwriteerror); |
||||
|
} else |
||||
|
launchProcess(new obj.zip.Deflater(), reader, writer, 0, reader.size, crcType, onprogress, onend, onreaderror, onwriteerror); |
||||
|
} |
||||
|
|
||||
|
function copy(worker, sn, reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) { |
||||
|
var crcType = 'input'; |
||||
|
if (obj.zip.useWebWorkers && computeCrc32) { |
||||
|
var initialMessage = { |
||||
|
sn: sn, |
||||
|
codecClass: 'NOOP', |
||||
|
crcType: crcType, |
||||
|
}; |
||||
|
launchWorkerProcess(worker, initialMessage, reader, writer, offset, size, onprogress, onend, onreaderror, onwriteerror); |
||||
|
} else |
||||
|
launchProcess(new NOOP(), reader, writer, offset, size, crcType, onprogress, onend, onreaderror, onwriteerror); |
||||
|
} |
||||
|
|
||||
|
// ZipReader
|
||||
|
|
||||
|
function decodeASCII(str) { |
||||
|
var i, out = "", charCode, extendedASCII = [ '\u00C7', '\u00FC', '\u00E9', '\u00E2', '\u00E4', '\u00E0', '\u00E5', '\u00E7', '\u00EA', '\u00EB', |
||||
|
'\u00E8', '\u00EF', '\u00EE', '\u00EC', '\u00C4', '\u00C5', '\u00C9', '\u00E6', '\u00C6', '\u00F4', '\u00F6', '\u00F2', '\u00FB', '\u00F9', |
||||
|
'\u00FF', '\u00D6', '\u00DC', '\u00F8', '\u00A3', '\u00D8', '\u00D7', '\u0192', '\u00E1', '\u00ED', '\u00F3', '\u00FA', '\u00F1', '\u00D1', |
||||
|
'\u00AA', '\u00BA', '\u00BF', '\u00AE', '\u00AC', '\u00BD', '\u00BC', '\u00A1', '\u00AB', '\u00BB', '_', '_', '_', '\u00A6', '\u00A6', |
||||
|
'\u00C1', '\u00C2', '\u00C0', '\u00A9', '\u00A6', '\u00A6', '+', '+', '\u00A2', '\u00A5', '+', '+', '-', '-', '+', '-', '+', '\u00E3', |
||||
|
'\u00C3', '+', '+', '-', '-', '\u00A6', '-', '+', '\u00A4', '\u00F0', '\u00D0', '\u00CA', '\u00CB', '\u00C8', 'i', '\u00CD', '\u00CE', |
||||
|
'\u00CF', '+', '+', '_', '_', '\u00A6', '\u00CC', '_', '\u00D3', '\u00DF', '\u00D4', '\u00D2', '\u00F5', '\u00D5', '\u00B5', '\u00FE', |
||||
|
'\u00DE', '\u00DA', '\u00DB', '\u00D9', '\u00FD', '\u00DD', '\u00AF', '\u00B4', '\u00AD', '\u00B1', '_', '\u00BE', '\u00B6', '\u00A7', |
||||
|
'\u00F7', '\u00B8', '\u00B0', '\u00A8', '\u00B7', '\u00B9', '\u00B3', '\u00B2', '_', ' ' ]; |
||||
|
for (i = 0; i < str.length; i++) { |
||||
|
charCode = str.charCodeAt(i) & 0xFF; |
||||
|
if (charCode > 127) |
||||
|
out += extendedASCII[charCode - 128]; |
||||
|
else |
||||
|
out += String.fromCharCode(charCode); |
||||
|
} |
||||
|
return out; |
||||
|
} |
||||
|
|
||||
|
function decodeUTF8(string) { |
||||
|
return decodeURIComponent(escape(string)); |
||||
|
} |
||||
|
|
||||
|
function getString(bytes) { |
||||
|
var i, str = ""; |
||||
|
for (i = 0; i < bytes.length; i++) |
||||
|
str += String.fromCharCode(bytes[i]); |
||||
|
return str; |
||||
|
} |
||||
|
|
||||
|
function getDate(timeRaw) { |
||||
|
var date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & 0x0000ffff; |
||||
|
try { |
||||
|
return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, |
||||
|
(time & 0x001F) * 2, 0); |
||||
|
} catch (e) { |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function readCommonHeader(entry, data, index, centralDirectory, onerror) { |
||||
|
entry.version = data.view.getUint16(index, true); |
||||
|
entry.bitFlag = data.view.getUint16(index + 2, true); |
||||
|
entry.compressionMethod = data.view.getUint16(index + 4, true); |
||||
|
entry.lastModDateRaw = data.view.getUint32(index + 6, true); |
||||
|
entry.lastModDate = getDate(entry.lastModDateRaw); |
||||
|
if ((entry.bitFlag & 0x01) === 0x01) { |
||||
|
onerror(ERR_ENCRYPTED); |
||||
|
return; |
||||
|
} |
||||
|
if (centralDirectory || (entry.bitFlag & 0x0008) != 0x0008) { |
||||
|
entry.crc32 = data.view.getUint32(index + 10, true); |
||||
|
entry.compressedSize = data.view.getUint32(index + 14, true); |
||||
|
entry.uncompressedSize = data.view.getUint32(index + 18, true); |
||||
|
} |
||||
|
if (entry.compressedSize === 0xFFFFFFFF || entry.uncompressedSize === 0xFFFFFFFF) { |
||||
|
onerror(ERR_ZIP64); |
||||
|
return; |
||||
|
} |
||||
|
entry.filenameLength = data.view.getUint16(index + 22, true); |
||||
|
entry.extraFieldLength = data.view.getUint16(index + 24, true); |
||||
|
} |
||||
|
|
||||
|
function createZipReader(reader, callback, onerror) { |
||||
|
var inflateSN = 0; |
||||
|
|
||||
|
function Entry() { |
||||
|
} |
||||
|
|
||||
|
Entry.prototype.getData = function(writer, onend, onprogress, checkCrc32) { |
||||
|
var that = this; |
||||
|
|
||||
|
function testCrc32(crc32) { |
||||
|
var dataCrc32 = getDataHelper(4); |
||||
|
dataCrc32.view.setUint32(0, crc32); |
||||
|
return that.crc32 == dataCrc32.view.getUint32(0); |
||||
|
} |
||||
|
|
||||
|
function getWriterData(uncompressedSize, crc32) { |
||||
|
if (checkCrc32 && !testCrc32(crc32)) |
||||
|
onerror(ERR_CRC); |
||||
|
else |
||||
|
writer.getData(function(data) { |
||||
|
onend(data); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function onreaderror(err) { |
||||
|
onerror(err || ERR_READ_DATA); |
||||
|
} |
||||
|
|
||||
|
function onwriteerror(err) { |
||||
|
onerror(err || ERR_WRITE_DATA); |
||||
|
} |
||||
|
|
||||
|
reader.readUint8Array(that.offset, 30, function(bytes) { |
||||
|
var data = getDataHelper(bytes.length, bytes), dataOffset; |
||||
|
if (data.view.getUint32(0) != 0x504b0304) { |
||||
|
onerror(ERR_BAD_FORMAT); |
||||
|
return; |
||||
|
} |
||||
|
readCommonHeader(that, data, 4, false, onerror); |
||||
|
dataOffset = that.offset + 30 + that.filenameLength + that.extraFieldLength; |
||||
|
writer.init(function() { |
||||
|
if (that.compressionMethod === 0) |
||||
|
copy(that._worker, inflateSN++, reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror); |
||||
|
else |
||||
|
inflate(that._worker, inflateSN++, reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror); |
||||
|
}, onwriteerror); |
||||
|
}, onreaderror); |
||||
|
}; |
||||
|
|
||||
|
function seekEOCDR(eocdrCallback) { |
||||
|
// "End of central directory record" is the last part of a zip archive, and is at least 22 bytes long.
|
||||
|
// Zip file comment is the last part of EOCDR and has max length of 64KB,
|
||||
|
// so we only have to search the last 64K + 22 bytes of a archive for EOCDR signature (0x06054b50).
|
||||
|
var EOCDR_MIN = 22; |
||||
|
if (reader.size < EOCDR_MIN) { |
||||
|
onerror(ERR_BAD_FORMAT); |
||||
|
return; |
||||
|
} |
||||
|
var ZIP_COMMENT_MAX = 256 * 256, EOCDR_MAX = EOCDR_MIN + ZIP_COMMENT_MAX; |
||||
|
|
||||
|
// In most cases, the EOCDR is EOCDR_MIN bytes long
|
||||
|
doSeek(EOCDR_MIN, function() { |
||||
|
// If not found, try within EOCDR_MAX bytes
|
||||
|
doSeek(Math.min(EOCDR_MAX, reader.size), function() { |
||||
|
onerror(ERR_BAD_FORMAT); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
// seek last length bytes of file for EOCDR
|
||||
|
function doSeek(length, eocdrNotFoundCallback) { |
||||
|
reader.readUint8Array(reader.size - length, length, function(bytes) { |
||||
|
for (var i = bytes.length - EOCDR_MIN; i >= 0; i--) { |
||||
|
if (bytes[i] === 0x50 && bytes[i + 1] === 0x4b && bytes[i + 2] === 0x05 && bytes[i + 3] === 0x06) { |
||||
|
eocdrCallback(new DataView(bytes.buffer, i, EOCDR_MIN)); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
eocdrNotFoundCallback(); |
||||
|
}, function() { |
||||
|
onerror(ERR_READ); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var zipReader = { |
||||
|
getEntries : function(callback) { |
||||
|
var worker = this._worker; |
||||
|
// look for End of central directory record
|
||||
|
seekEOCDR(function(dataView) { |
||||
|
var datalength, fileslength; |
||||
|
datalength = dataView.getUint32(16, true); |
||||
|
fileslength = dataView.getUint16(8, true); |
||||
|
if (datalength < 0 || datalength >= reader.size) { |
||||
|
onerror(ERR_BAD_FORMAT); |
||||
|
return; |
||||
|
} |
||||
|
reader.readUint8Array(datalength, reader.size - datalength, function(bytes) { |
||||
|
var i, index = 0, entries = [], entry, filename, comment, data = getDataHelper(bytes.length, bytes); |
||||
|
for (i = 0; i < fileslength; i++) { |
||||
|
entry = new Entry(); |
||||
|
entry._worker = worker; |
||||
|
if (data.view.getUint32(index) != 0x504b0102) { |
||||
|
onerror(ERR_BAD_FORMAT); |
||||
|
return; |
||||
|
} |
||||
|
readCommonHeader(entry, data, index + 6, true, onerror); |
||||
|
entry.commentLength = data.view.getUint16(index + 32, true); |
||||
|
entry.directory = ((data.view.getUint8(index + 38) & 0x10) == 0x10); |
||||
|
entry.offset = data.view.getUint32(index + 42, true); |
||||
|
filename = getString(data.array.subarray(index + 46, index + 46 + entry.filenameLength)); |
||||
|
entry.filename = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(filename) : decodeASCII(filename); |
||||
|
if (!entry.directory && entry.filename.charAt(entry.filename.length - 1) == "/") |
||||
|
entry.directory = true; |
||||
|
comment = getString(data.array.subarray(index + 46 + entry.filenameLength + entry.extraFieldLength, index + 46 |
||||
|
+ entry.filenameLength + entry.extraFieldLength + entry.commentLength)); |
||||
|
entry.comment = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(comment) : decodeASCII(comment); |
||||
|
entries.push(entry); |
||||
|
index += 46 + entry.filenameLength + entry.extraFieldLength + entry.commentLength; |
||||
|
} |
||||
|
callback(entries); |
||||
|
}, function() { |
||||
|
onerror(ERR_READ); |
||||
|
}); |
||||
|
}); |
||||
|
}, |
||||
|
close : function(callback) { |
||||
|
if (this._worker) { |
||||
|
this._worker.terminate(); |
||||
|
this._worker = null; |
||||
|
} |
||||
|
if (callback) |
||||
|
callback(); |
||||
|
}, |
||||
|
_worker: null |
||||
|
}; |
||||
|
|
||||
|
if (!obj.zip.useWebWorkers) |
||||
|
callback(zipReader); |
||||
|
else { |
||||
|
createWorker('inflater', |
||||
|
function(worker) { |
||||
|
zipReader._worker = worker; |
||||
|
callback(zipReader); |
||||
|
}, |
||||
|
function(err) { |
||||
|
onerror(err); |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ZipWriter
|
||||
|
|
||||
|
function encodeUTF8(string) { |
||||
|
return unescape(encodeURIComponent(string)); |
||||
|
} |
||||
|
|
||||
|
function getBytes(str) { |
||||
|
var i, array = []; |
||||
|
for (i = 0; i < str.length; i++) |
||||
|
array.push(str.charCodeAt(i)); |
||||
|
return array; |
||||
|
} |
||||
|
|
||||
|
function createZipWriter(writer, callback, onerror, dontDeflate) { |
||||
|
var files = {}, filenames = [], datalength = 0; |
||||
|
var deflateSN = 0; |
||||
|
|
||||
|
function onwriteerror(err) { |
||||
|
onerror(err || ERR_WRITE); |
||||
|
} |
||||
|
|
||||
|
function onreaderror(err) { |
||||
|
onerror(err || ERR_READ_DATA); |
||||
|
} |
||||
|
|
||||
|
var zipWriter = { |
||||
|
add : function(name, reader, onend, onprogress, options) { |
||||
|
var header, filename, date; |
||||
|
var worker = this._worker; |
||||
|
|
||||
|
function writeHeader(callback) { |
||||
|
var data; |
||||
|
date = options.lastModDate || new Date(); |
||||
|
header = getDataHelper(26); |
||||
|
files[name] = { |
||||
|
headerArray : header.array, |
||||
|
directory : options.directory, |
||||
|
filename : filename, |
||||
|
offset : datalength, |
||||
|
comment : getBytes(encodeUTF8(options.comment || "")) |
||||
|
}; |
||||
|
header.view.setUint32(0, 0x14000808); |
||||
|
if (options.version) |
||||
|
header.view.setUint8(0, options.version); |
||||
|
if (!dontDeflate && options.level !== 0 && !options.directory) |
||||
|
header.view.setUint16(4, 0x0800); |
||||
|
header.view.setUint16(6, (((date.getHours() << 6) | date.getMinutes()) << 5) | date.getSeconds() / 2, true); |
||||
|
header.view.setUint16(8, ((((date.getFullYear() - 1980) << 4) | (date.getMonth() + 1)) << 5) | date.getDate(), true); |
||||
|
header.view.setUint16(22, filename.length, true); |
||||
|
data = getDataHelper(30 + filename.length); |
||||
|
data.view.setUint32(0, 0x504b0304); |
||||
|
data.array.set(header.array, 4); |
||||
|
data.array.set(filename, 30); |
||||
|
datalength += data.array.length; |
||||
|
writer.writeUint8Array(data.array, callback, onwriteerror); |
||||
|
} |
||||
|
|
||||
|
function writeFooter(compressedLength, crc32) { |
||||
|
var footer = getDataHelper(16); |
||||
|
datalength += compressedLength || 0; |
||||
|
footer.view.setUint32(0, 0x504b0708); |
||||
|
if (typeof crc32 != "undefined") { |
||||
|
header.view.setUint32(10, crc32, true); |
||||
|
footer.view.setUint32(4, crc32, true); |
||||
|
} |
||||
|
if (reader) { |
||||
|
footer.view.setUint32(8, compressedLength, true); |
||||
|
header.view.setUint32(14, compressedLength, true); |
||||
|
footer.view.setUint32(12, reader.size, true); |
||||
|
header.view.setUint32(18, reader.size, true); |
||||
|
} |
||||
|
writer.writeUint8Array(footer.array, function() { |
||||
|
datalength += 16; |
||||
|
onend(); |
||||
|
}, onwriteerror); |
||||
|
} |
||||
|
|
||||
|
function writeFile() { |
||||
|
options = options || {}; |
||||
|
name = name.trim(); |
||||
|
if (options.directory && name.charAt(name.length - 1) != "/") |
||||
|
name += "/"; |
||||
|
if (files.hasOwnProperty(name)) { |
||||
|
onerror(ERR_DUPLICATED_NAME); |
||||
|
return; |
||||
|
} |
||||
|
filename = getBytes(encodeUTF8(name)); |
||||
|
filenames.push(name); |
||||
|
writeHeader(function() { |
||||
|
if (reader) |
||||
|
if (dontDeflate || options.level === 0) |
||||
|
copy(worker, deflateSN++, reader, writer, 0, reader.size, true, writeFooter, onprogress, onreaderror, onwriteerror); |
||||
|
else |
||||
|
deflate(worker, deflateSN++, reader, writer, options.level, writeFooter, onprogress, onreaderror, onwriteerror); |
||||
|
else |
||||
|
writeFooter(); |
||||
|
}, onwriteerror); |
||||
|
} |
||||
|
|
||||
|
if (reader) |
||||
|
reader.init(writeFile, onreaderror); |
||||
|
else |
||||
|
writeFile(); |
||||
|
}, |
||||
|
close : function(callback) { |
||||
|
if (this._worker) { |
||||
|
this._worker.terminate(); |
||||
|
this._worker = null; |
||||
|
} |
||||
|
|
||||
|
var data, length = 0, index = 0, indexFilename, file; |
||||
|
for (indexFilename = 0; indexFilename < filenames.length; indexFilename++) { |
||||
|
file = files[filenames[indexFilename]]; |
||||
|
length += 46 + file.filename.length + file.comment.length; |
||||
|
} |
||||
|
data = getDataHelper(length + 22); |
||||
|
for (indexFilename = 0; indexFilename < filenames.length; indexFilename++) { |
||||
|
file = files[filenames[indexFilename]]; |
||||
|
data.view.setUint32(index, 0x504b0102); |
||||
|
data.view.setUint16(index + 4, 0x1400); |
||||
|
data.array.set(file.headerArray, index + 6); |
||||
|
data.view.setUint16(index + 32, file.comment.length, true); |
||||
|
if (file.directory) |
||||
|
data.view.setUint8(index + 38, 0x10); |
||||
|
data.view.setUint32(index + 42, file.offset, true); |
||||
|
data.array.set(file.filename, index + 46); |
||||
|
data.array.set(file.comment, index + 46 + file.filename.length); |
||||
|
index += 46 + file.filename.length + file.comment.length; |
||||
|
} |
||||
|
data.view.setUint32(index, 0x504b0506); |
||||
|
data.view.setUint16(index + 8, filenames.length, true); |
||||
|
data.view.setUint16(index + 10, filenames.length, true); |
||||
|
data.view.setUint32(index + 12, length, true); |
||||
|
data.view.setUint32(index + 16, datalength, true); |
||||
|
writer.writeUint8Array(data.array, function() { |
||||
|
writer.getData(callback); |
||||
|
}, onwriteerror); |
||||
|
}, |
||||
|
_worker: null |
||||
|
}; |
||||
|
|
||||
|
if (!obj.zip.useWebWorkers) |
||||
|
callback(zipWriter); |
||||
|
else { |
||||
|
createWorker('deflater', |
||||
|
function(worker) { |
||||
|
zipWriter._worker = worker; |
||||
|
callback(zipWriter); |
||||
|
}, |
||||
|
function(err) { |
||||
|
onerror(err); |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function resolveURLs(urls) { |
||||
|
var a = document.createElement('a'); |
||||
|
return urls.map(function(url) { |
||||
|
a.href = url; |
||||
|
return a.href; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
var DEFAULT_WORKER_SCRIPTS = { |
||||
|
deflater: ['z-worker.js', 'deflate.js'], |
||||
|
inflater: ['z-worker.js', 'inflate.js'] |
||||
|
}; |
||||
|
function createWorker(type, callback, onerror) { |
||||
|
if (obj.zip.workerScripts !== null && obj.zip.workerScriptsPath !== null) { |
||||
|
onerror(new Error('Either zip.workerScripts or zip.workerScriptsPath may be set, not both.')); |
||||
|
return; |
||||
|
} |
||||
|
var scripts; |
||||
|
if (obj.zip.workerScripts) { |
||||
|
scripts = obj.zip.workerScripts[type]; |
||||
|
if (!Array.isArray(scripts)) { |
||||
|
onerror(new Error('zip.workerScripts.' + type + ' is not an array!')); |
||||
|
return; |
||||
|
} |
||||
|
scripts = resolveURLs(scripts); |
||||
|
} else { |
||||
|
scripts = DEFAULT_WORKER_SCRIPTS[type].slice(0); |
||||
|
scripts[0] = (obj.zip.workerScriptsPath || '') + scripts[0]; |
||||
|
} |
||||
|
var worker = new Worker(scripts[0]); |
||||
|
// record total consumed time by inflater/deflater/crc32 in this worker
|
||||
|
worker.codecTime = worker.crcTime = 0; |
||||
|
worker.postMessage({ type: 'importScripts', scripts: scripts.slice(1) }); |
||||
|
worker.addEventListener('message', onmessage); |
||||
|
function onmessage(ev) { |
||||
|
var msg = ev.data; |
||||
|
if (msg.error) { |
||||
|
worker.terminate(); // should before onerror(), because onerror() may throw.
|
||||
|
onerror(msg.error); |
||||
|
return; |
||||
|
} |
||||
|
if (msg.type === 'importScripts') { |
||||
|
worker.removeEventListener('message', onmessage); |
||||
|
worker.removeEventListener('error', errorHandler); |
||||
|
callback(worker); |
||||
|
} |
||||
|
} |
||||
|
// catch entry script loading error and other unhandled errors
|
||||
|
worker.addEventListener('error', errorHandler); |
||||
|
function errorHandler(err) { |
||||
|
worker.terminate(); |
||||
|
onerror(err); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function onerror_default(error) { |
||||
|
console.error(error); |
||||
|
} |
||||
|
obj.zip = { |
||||
|
Reader : Reader, |
||||
|
Writer : Writer, |
||||
|
BlobReader : BlobReader, |
||||
|
Data64URIReader : Data64URIReader, |
||||
|
TextReader : TextReader, |
||||
|
BlobWriter : BlobWriter, |
||||
|
Data64URIWriter : Data64URIWriter, |
||||
|
TextWriter : TextWriter, |
||||
|
createReader : function(reader, callback, onerror) { |
||||
|
onerror = onerror || onerror_default; |
||||
|
|
||||
|
reader.init(function() { |
||||
|
createZipReader(reader, callback, onerror); |
||||
|
}, onerror); |
||||
|
}, |
||||
|
createWriter : function(writer, callback, onerror, dontDeflate) { |
||||
|
onerror = onerror || onerror_default; |
||||
|
dontDeflate = !!dontDeflate; |
||||
|
|
||||
|
writer.init(function() { |
||||
|
createZipWriter(writer, callback, onerror, dontDeflate); |
||||
|
}, onerror); |
||||
|
}, |
||||
|
useWebWorkers : true, |
||||
|
/** |
||||
|
* Directory containing the default worker scripts (z-worker.js, deflate.js, and inflate.js), relative to current base url. |
||||
|
* E.g.: zip.workerScripts = './'; |
||||
|
*/ |
||||
|
workerScriptsPath : null, |
||||
|
/** |
||||
|
* Advanced option to control which scripts are loaded in the Web worker. If this option is specified, then workerScriptsPath must not be set. |
||||
|
* workerScripts.deflater/workerScripts.inflater should be arrays of urls to scripts for deflater/inflater, respectively. |
||||
|
* Scripts in the array are executed in order, and the first one should be z-worker.js, which is used to start the worker. |
||||
|
* All urls are relative to current base url. |
||||
|
* E.g.: |
||||
|
* zip.workerScripts = { |
||||
|
* deflater: ['z-worker.js', 'deflate.js'], |
||||
|
* inflater: ['z-worker.js', 'inflate.js'] |
||||
|
* }; |
||||
|
*/ |
||||
|
workerScripts : null, |
||||
|
}; |
||||
|
|
||||
|
})(this); |
@ -0,0 +1,213 @@ |
|||||
|
/* License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). */ |
||||
|
|
||||
|
|
||||
|
odoo.define("pos_jsprintmanager.screen", function (require) { |
||||
|
"use strict"; |
||||
|
|
||||
|
var core = require('web.core'); |
||||
|
var _t = core._t; |
||||
|
var PosBaseWidget = require('point_of_sale.BaseWidget'); |
||||
|
var screens = require('point_of_sale.screens'); |
||||
|
|
||||
|
screens.ReceiptScreenWidget.include({ |
||||
|
|
||||
|
//Check JSPM WebSocket status
|
||||
|
jspmWSStatus: function() { |
||||
|
if (JSPM.JSPrintManager.websocket_status == JSPM.WSStatus.Open) |
||||
|
return true; |
||||
|
else if (JSPM.JSPrintManager.websocket_status == JSPM.WSStatus.Closed) { |
||||
|
alert('JSPrintManager (JSPM) is not installed or not running! Download JSPM Client App from https://neodynamic.com/downloads/jspm'); |
||||
|
return false; |
||||
|
} |
||||
|
else if (JSPM.JSPrintManager.websocket_status == JSPM.WSStatus.BlackListed) { |
||||
|
alert('JSPM has blacklisted this website!'); |
||||
|
return false; |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
init: function(parent,options){ |
||||
|
this._super(parent,options); |
||||
|
JSPM.JSPrintManager.auto_reconnect = true; |
||||
|
JSPM.JSPrintManager.start(); |
||||
|
var jspmWSStatus = this.jspmWSStatus() |
||||
|
JSPM.JSPrintManager.WS.onStatusChanged = function () { |
||||
|
if (jspmWSStatus) { |
||||
|
//get client installed printers
|
||||
|
JSPM.JSPrintManager.getPrinters().then(function (myPrinters) { |
||||
|
var options = ''; |
||||
|
for (var i = 0; i < myPrinters.length; i++) { |
||||
|
options += '<option>' + myPrinters[i] + '</option>'; |
||||
|
} |
||||
|
$('#installedPrinterName').html(options); |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Variables definition
|
||||
|
this.esc = '\x1B'; //ESC byte in hex notation
|
||||
|
this.line_feed = '\x0A'; //LF byte in hex notation
|
||||
|
|
||||
|
this.page_width = 48; |
||||
|
this.qty_width = 12; |
||||
|
this.price_width = 12; |
||||
|
this.totals_width = 24; |
||||
|
this.name_width = this.page_width - this.qty_width - this.price_width; |
||||
|
|
||||
|
}, |
||||
|
center_align_string: function(s, width) { |
||||
|
return s.padStart( |
||||
|
Math.floor((width - s.length) / 2 + s.length), |
||||
|
' ' |
||||
|
); |
||||
|
}, |
||||
|
right_align_string: function(s, width) { |
||||
|
return s.padStart(width, ' '); |
||||
|
}, |
||||
|
get_escpos_receipt_cmds: function() { |
||||
|
var cmds = ''; |
||||
|
var order = this.pos.get_order(); |
||||
|
var receipt = order.export_for_printing(); |
||||
|
var orderlines = order.get_orderlines(); |
||||
|
var paymentlines = order.get_paymentlines(); |
||||
|
|
||||
|
//Initializes the printer (ESC @)
|
||||
|
cmds += this.esc + "@"; |
||||
|
cmds += this.esc + "\x70" + "\x00"; // Drawer kick, pin 2 (first drawer)
|
||||
|
cmds += this.esc + "1\x02" // Codepage 850
|
||||
|
|
||||
|
// Header of receipt with Company data
|
||||
|
cmds += receipt.company.contact_address ? receipt.company.contact_address + this.line_feed : ""; |
||||
|
cmds += receipt.company.phone ? _t("Tel: ") + receipt.company.phone + this.line_feed : ""; |
||||
|
cmds += receipt.company.vat ? _t("VAT: ") + receipt.company.vat + this.line_feed : ""; |
||||
|
cmds += receipt.company.email ? receipt.company.email + this.line_feed : ""; |
||||
|
cmds += receipt.company.website ? receipt.company.website + this.line_feed : ""; |
||||
|
cmds += receipt.company.header ? receipt.company.header + this.line_feed : ""; |
||||
|
cmds += this.line_feed + this.line_feed; |
||||
|
|
||||
|
// Date and Order ID
|
||||
|
cmds += this.center_align_string( |
||||
|
receipt.date.localestring + ' ' + receipt.name, |
||||
|
this.page_width |
||||
|
); |
||||
|
cmds += this.line_feed + this.line_feed; |
||||
|
|
||||
|
// Order Lines
|
||||
|
for (const line of orderlines) { |
||||
|
let name = line.get_product().display_name; |
||||
|
let qty = "" + line.get_quantity_str_with_unit(); |
||||
|
let price = "" + this.format_currency(line.get_display_price()); |
||||
|
|
||||
|
cmds += name.substring(0, this.name_width).padEnd(this.name_width, ' '); |
||||
|
cmds += this.right_align_string(qty, this.qty_width); |
||||
|
cmds += this.right_align_string(price, this.price_width); |
||||
|
cmds += this.line_feed; |
||||
|
|
||||
|
if (line.discount > 0) { |
||||
|
cmds += _(' - with a ') + line.discountStr + '% ' + _(' discount'); |
||||
|
cmds += this.line_feed; |
||||
|
} |
||||
|
} |
||||
|
cmds += this.line_feed; |
||||
|
|
||||
|
// Subtotal
|
||||
|
var total_without_tax = this.format_currency(order.get_total_without_tax()); |
||||
|
cmds += _t("Subtotal: ").padEnd(this.totals_width, ' '); |
||||
|
cmds += this.right_align_string(this.format_currency(order.get_total_without_tax()), this.totals_width) |
||||
|
cmds += this.line_feed; |
||||
|
|
||||
|
// Taxes
|
||||
|
for (const taxdetail of order.get_tax_details()) { |
||||
|
cmds += taxdetail.name.padEnd(this.totals_width, ' '); |
||||
|
cmds += this.right_align_string(this.format_currency(taxdetail.amount), this.totals_width); |
||||
|
} |
||||
|
cmds += this.line_feed; |
||||
|
|
||||
|
// Discount
|
||||
|
if (order.get_total_discount() > 0) { |
||||
|
cmds += _t("Discount: ").padEnd(this.totals_width, ' '); |
||||
|
cmds += this.right_align_string(this.format_currency(order.get_total_discount()), this.totals_width); |
||||
|
} |
||||
|
cmds += this.line_feed; |
||||
|
|
||||
|
// Total amount
|
||||
|
cmds += this.esc + '!' + '\x10'; // Double-height
|
||||
|
cmds += _t("Total: ").padEnd(this.totals_width, ' '); |
||||
|
cmds += this.right_align_string(this.format_currency(order.get_total_with_tax()), this.totals_width); |
||||
|
cmds += this.esc + '!' + '\x00'; // Normal character
|
||||
|
cmds += this.line_feed + this.line_feed; |
||||
|
|
||||
|
|
||||
|
// Payment Lines
|
||||
|
for (const line of paymentlines) { |
||||
|
cmds += line.name.padEnd(this.totals_width, ' '); |
||||
|
cmds += this.right_align_string(this.format_currency(line.get_amount()), this.totals_width); |
||||
|
} |
||||
|
cmds += this.line_feed; |
||||
|
|
||||
|
|
||||
|
// Change
|
||||
|
cmds += _t("Change: ").padEnd(this.totals_width, ' '); |
||||
|
cmds += this.right_align_string(this.format_currency(order.get_change()), this.totals_width); |
||||
|
cmds += this.line_feed; |
||||
|
|
||||
|
return cmds |
||||
|
|
||||
|
}, |
||||
|
get_escpos_receipt_cmds_footer: function () { |
||||
|
var cmds = ''; |
||||
|
var order = this.pos.get_order(); |
||||
|
var receipt = order.export_for_printing(); |
||||
|
|
||||
|
if (receipt.footer) { |
||||
|
cmds += receipt.footer; |
||||
|
} |
||||
|
|
||||
|
cmds += this.line_feed + this.line_feed + this.line_feed + this.line_feed; // Space before cut
|
||||
|
cmds += "\x1dV\x00"; // Full cut
|
||||
|
cmds += this.line_feed + this.line_feed; |
||||
|
|
||||
|
return cmds |
||||
|
}, |
||||
|
print_web: function() { |
||||
|
if (this.jspmWSStatus && this.pos.config.use_jsprintmanager == true) { |
||||
|
var outputFormat = this.pos.config.jsprintmanager_output_format; |
||||
|
var default_printer = this.pos.config.jsprintmanager_default_receipt_printer; |
||||
|
console.log(outputFormat) |
||||
|
//Create a ClientPrintJob
|
||||
|
var cpj = new JSPM.ClientPrintJob(); |
||||
|
if (default_printer) { |
||||
|
cpj.clientPrinter = new JSPM.InstalledPrinter(default_printer); |
||||
|
} else { |
||||
|
cpj.clientPrinter = new JSPM.DefaultPrinter(); |
||||
|
} |
||||
|
if (outputFormat == 'escpos'){ |
||||
|
//Set content to print...
|
||||
|
//Create ESP/POS commands for sample label
|
||||
|
var cmds = [this.get_escpos_receipt_cmds(), this.get_escpos_receipt_cmds_footer()].join("") |
||||
|
cpj.printerCommands = cmds; |
||||
|
//Send print job to printer!
|
||||
|
cpj.sendToClient(); |
||||
|
} else { |
||||
|
//generate an image of HTML content through html2canvas utility
|
||||
|
var ticket = document.getElementsByClassName('pos-sale-ticket')[0] |
||||
|
html2canvas(ticket, {scale: 10, width: 900}).then(function (canvas) { |
||||
|
//Set content to print...
|
||||
|
var b64Prefix = "data:image/png;base64,"; |
||||
|
var imgBase64DataUri = canvas.toDataURL("image/png"); |
||||
|
var imgBase64Content = imgBase64DataUri.substring(b64Prefix.length, imgBase64DataUri.length); |
||||
|
var myImageFile = new JSPM.PrintFile(imgBase64Content, JSPM.FileSourceType.Base64, 'myFileToPrint.png', 1); |
||||
|
//add file to print job
|
||||
|
cpj.files.push(myImageFile); |
||||
|
//Send print job to printer!
|
||||
|
cpj.sendToClient(); |
||||
|
}); |
||||
|
} |
||||
|
this.pos.get_order()._printed = true; |
||||
|
} else { |
||||
|
return this._super(); |
||||
|
} |
||||
|
|
||||
|
}, |
||||
|
}) |
||||
|
}); |
||||
|
|
@ -0,0 +1,20 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<!-- |
||||
|
Copyright (C) 2020-Today: ForgeFlow, S.L. (<https://www.forgeflow.com/>) |
||||
|
@author: Jordi Ballester Alomar (https://twitter.com/jordibforgeflow) |
||||
|
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). |
||||
|
--> |
||||
|
|
||||
|
<odoo> |
||||
|
<template id="assets_frontend" inherit_id="point_of_sale.assets"> |
||||
|
<xpath expr="." position="inside"> |
||||
|
<script type="text/javascript" src="/pos_jsprintmanager/static/src/js/jsprintmanager/zip.js"/> |
||||
|
<script type="text/javascript" src="/pos_jsprintmanager/static/src/js/jsprintmanager/zip-ext.js"/> |
||||
|
<script type="text/javascript" src="/pos_jsprintmanager/static/src/js/jsprintmanager/deflate.js"/> |
||||
|
<script type="text/javascript" src="/pos_jsprintmanager/static/src/js/jsprintmanager/JSPrintManager.js"/> |
||||
|
<script type="text/javascript" src="/pos_jsprintmanager/static/src/js/jsprintmanager/html2canvas.min.js"/> |
||||
|
<script type="text/javascript" src="/pos_jsprintmanager/static/src/js/jsprintmanager/bluebird.min.js"/> |
||||
|
<script type="text/javascript" src="/pos_jsprintmanager/static/src/js/screen.js"/> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
</odoo> |
@ -0,0 +1,41 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
|
||||
|
<odoo> |
||||
|
|
||||
|
<record id="view_pos_config_view_form_jsprintmanager" model="ir.ui.view"> |
||||
|
<field name="name">pos.config.form.jsprintmanager</field> |
||||
|
<field name="model">pos.config</field> |
||||
|
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/> |
||||
|
<field name="priority" eval="30"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<xpath expr="//div[@id='receipt']" position="inside"> |
||||
|
<div class="col-12 col-lg-6 o_setting_box" id="use_jsprintmanager"> |
||||
|
<div class="o_setting_left_pane"> |
||||
|
<field name="use_jsprintmanager"/> |
||||
|
</div> |
||||
|
<div class="o_setting_right_pane"> |
||||
|
<label for="use_jsprintmanager"/> |
||||
|
<div class="text-muted"> |
||||
|
Use JS Print Manager to print receipts |
||||
|
</div> |
||||
|
</div> |
||||
|
<div id='jsprintmanager_default_receipt_printer' class="o_setting_right_pane" attrs="{'invisible' : [('use_jsprintmanager', '=', False)]}"> |
||||
|
<span class="o_form_label">Default Printer for Receipts</span> |
||||
|
<div class="content-group mt16"> |
||||
|
<field name="jsprintmanager_default_receipt_printer" colspan="4" |
||||
|
nolabel="1"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div id='jsprintmanager_output_format' class="o_setting_right_pane" attrs="{'invisible' : [('use_jsprintmanager', '=', False)]}"> |
||||
|
<span class="o_form_label">Output format</span> |
||||
|
<div class="content-group mt16"> |
||||
|
<field name="jsprintmanager_output_format" colspan="4" |
||||
|
nolabel="1"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</xpath> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue