ajout croquis_edit
This commit is contained in:
@@ -357,13 +357,13 @@ def insert_dem_note(request, nodossier, type_note, logged_in):
|
||||
query = "INSERT INTO dem_notes (societe, nochantier, type_note, usermaj) VALUES (:societe, :nochantier, :type_note, :logged_in);"
|
||||
execute_query(request, query, {'societe': societe, 'nochantier': nochantier, 'type_note': type_note, 'logged_in': logged_in})
|
||||
|
||||
def get_dem_notes(request, nodossier, noligne):
|
||||
def get_dem_notes(request, nodossier, noligne, type):
|
||||
societe = nodossier[0:2]
|
||||
nochantier = int(nodossier[3:])
|
||||
|
||||
if noligne == '0':
|
||||
query = "SELECT societe, nochantier, noligne, type_note, libelle FROM dem_notes WHERE societe = :societe AND nochantier = :nochantier ORDER BY libelle;"
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'nochantier': nochantier, 'noligne': noligne}).fetchall()
|
||||
query = "SELECT societe, nochantier, noligne, type_note, libelle FROM dem_notes WHERE societe = :societe AND nochantier = :nochantier AND type_note=:type ORDER BY libelle;"
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'nochantier': nochantier, 'noligne': noligne, 'type': type}).fetchall()
|
||||
else:
|
||||
query = "SELECT * FROM dem_notes WHERE societe = :societe AND nochantier = :nochantier AND noligne = :noligne;"
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'nochantier': nochantier, 'noligne': noligne}).first()
|
||||
|
||||
@@ -34,6 +34,7 @@ def includeme(config):
|
||||
config.add_route('facture_select', '/facture_select/{date}')
|
||||
config.add_route('facture_selected', '/facture_selected/{goto}/{date}/{nofacture}')
|
||||
# dossier
|
||||
config.add_route('croquis_edit','/croquis_edit/')
|
||||
config.add_route('demandes','/demandes')
|
||||
config.add_route('demandes_dl','/demandes_dl/{societe}/{email_from}/{email_uid}')
|
||||
config.add_route('dem_devis','/dem_devis')
|
||||
@@ -43,6 +44,7 @@ def includeme(config):
|
||||
config.add_route('dossier_select', '/dossier_select/{date}')
|
||||
config.add_route('dossier_selected', '/dossier_selected/{goto}/{date}/{nodossier}')
|
||||
config.add_route('dossier_view', '/dossier_view/{nodossier}')
|
||||
config.add_route('note_add','/note_add/{nodossier}')
|
||||
config.add_route('note_edit','/note_edit/{nodossier}/{noligne}')
|
||||
config.add_route('rdf_bill','/rdf_bill/{no_id}')
|
||||
config.add_route('rdf_client','/rdf_client/{no_id}')
|
||||
|
||||
@@ -276,6 +276,10 @@ color: black;
|
||||
}
|
||||
}
|
||||
|
||||
#dessin {
|
||||
width: 1140px;
|
||||
height: 1140px;
|
||||
}
|
||||
|
||||
/* ne pas affichier l'url after the link */
|
||||
@media print {
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/* drawingboard.js v0.4.6 - https://github.com/Leimi/drawingboard.js
|
||||
* Copyright (c) 2015 Emmanuel Pelletier
|
||||
* Licensed MIT */
|
||||
.drawing-board, .drawing-board * { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; }
|
||||
|
||||
.drawing-board-utils-hidden, .drawing-board-controls-hidden { display: none !important; }
|
||||
|
||||
.drawing-board { position: relative; display: block; }
|
||||
|
||||
.drawing-board-canvas-wrapper { position: relative; margin: 0; border: 1px solid #ddd; }
|
||||
|
||||
.drawing-board-canvas { position: absolute; top: 0; left: 0; z-index: 10; width: auto; }
|
||||
|
||||
.drawing-board-canvas { cursor: crosshair; z-index: 20; }
|
||||
|
||||
.drawing-board-cursor { position: absolute; top: 0; left: 0; pointer-events: none; border-radius: 50%; background: #ccc; background: rgba(0, 0, 0, 0.2); z-index: 30; }
|
||||
|
||||
.drawing-board-control > button, .drawing-board-control-colors-rainbows, .drawing-board-control-size .drawing-board-control-inner, .drawing-board-control-size-dropdown { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; overflow: hidden; border: none; background-color: #eee; padding: 2px 4px; border: 1px solid #ccc; box-shadow: 0 1px 3px -2px #121212, inset 0 2px 5px 0 rgba(255, 255, 255, 0.3); -webkit-box-shadow: 0 1px 3px -2px #121212, inset 0 2px 5px 0 rgba(255, 255, 255, 0.3); height: 28px; }
|
||||
|
||||
.drawing-board-control > button { cursor: pointer; min-width: 28px; line-height: 14px; }
|
||||
.drawing-board-control > button:hover, .drawing-board-control > button:focus { background-color: #ddd; }
|
||||
.drawing-board-control > button:active, .drawing-board-control > button.active { box-shadow: inset 0 1px 2px 0 rgba(0, 0, 0, 0.2); -webkit-box-shadow: inset 0 1px 2px 0 rgba(0, 0, 0, 0.2); background-color: #ddd; }
|
||||
.drawing-board-control > button[disabled] { color: gray; }
|
||||
.drawing-board-control > button[disabled]:hover, .drawing-board-control > button[disabled]:focus, .drawing-board-control > button[disabled]:active, .drawing-board-control > button[disabled].active { background-color: #eee; box-shadow: 0 1px 3px -2px #121212, inset 0 2px 5px 0 rgba(255, 255, 255, 0.3); -webkit-box-shadow: 0 1px 3px -2px #121212, inset 0 2px 5px 0 rgba(255, 255, 255, 0.3); cursor: default; }
|
||||
|
||||
.drawing-board-controls { margin: 0 auto; text-align: center; font-size: 0; display: table; border-spacing: 9.33333px 0; position: relative; min-height: 28px; }
|
||||
.drawing-board-controls[data-align="left"] { margin: 0; left: -9.33333px; }
|
||||
.drawing-board-controls[data-align="right"] { margin: 0 0 0 auto; right: -9.33333px; }
|
||||
.drawing-board-canvas-wrapper + .drawing-board-controls, .drawing-board-controls + .drawing-board-canvas-wrapper { margin-top: 5px; }
|
||||
|
||||
.drawing-board-controls-hidden { height: 0; min-height: 0; padding: 0; margin: 0; border: 0; }
|
||||
|
||||
.drawing-board-control { display: table-cell; border-collapse: separate; vertical-align: middle; font-size: 16px; height: 100%; }
|
||||
|
||||
.drawing-board-control-inner { position: relative; height: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
|
||||
|
||||
.drawing-board-control > button { margin: 0; vertical-align: middle; }
|
||||
|
||||
.drawing-board-control-colors { font-size: 0; line-height: 0; }
|
||||
|
||||
.drawing-board-control-colors-current { border: 1px solid #ccc; cursor: pointer; display: inline-block; width: 26px; height: 26px; }
|
||||
|
||||
.drawing-board-control-colors-rainbows { display: inline-block; margin-left: 5px; position: absolute; left: 0; top: 33px; margin-left: 0; z-index: 100; width: 250px; height: auto; padding: 4px; }
|
||||
|
||||
.drawing-board-control-colors-rainbow { height: 18px; }
|
||||
|
||||
.drawing-board-control-colors-picker:first-child { margin-right: 5px; }
|
||||
|
||||
.drawing-board-control-colors-picker { display: inline-block; width: 18px; height: 18px; cursor: pointer; }
|
||||
|
||||
.drawing-board-control-colors-picker[data-color="rgba(255, 255, 255, 1)"] { width: 16px; height: 17px; border: 1px solid #ccc; border-bottom: none; }
|
||||
|
||||
.drawing-board-control-colors-picker:hover { width: 16px; height: 16px; border: 1px solid #555; }
|
||||
|
||||
.drawing-board-control-drawingmode > button { margin-right: 2px; }
|
||||
.drawing-board-control-drawingmode > button:last-child { margin-right: 0; }
|
||||
|
||||
.drawing-board-control-drawingmode-pencil-button { overflow: hidden; *text-indent: -9999px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAe9JREFUeNpiZAACVlFRBhYREQZcQPnbNwa3N28YlL5+ZfgLFfvPwGD9m4FhIgsDHuAO0gTUDNKIBvyBmqt/MTDMY8Gl0f31azD7L6oUIxCnAzWmAPHBfwwM01AMUAV6JfPQIVwOYgVqqPnFyOjz6///O38YGKpAgmAD1OXlGdTk5PD5hgeouZudj8/uy9evP/78/dsFFPsJNiAoKIiBABAHap4oLi9v8fTNm48//v7NBwbgWZgkE7rqt8DY+A8JZRBW+cfIuEDT0NDlzadP3z98/doPFDuCrB7TAGFhBqCNIGwM9OcKUzs7+xdv3355+f79VqDYAiTDwZgJh7ONgYpnOvn4GL949erT7UePdgL5JVCD4fgBLBBxaX74+PG789evnwby0/8jKXgExIeB+CG6Af///1e9Ki9vFSAkZPzoyZPPJy9evA9MB77/sWiEARZkzV+/fvXYtGnTpG3btj28EBT0BqjZ5D8OjXCwPksUhA1Wpggf/PHjx/9169Y9EBERaUlgZmaIAcrLE4rk5sIqBqDmlefnRPzfWGX5EaSZm5ubgRloADGA5QZ3RgK7gESY4PMNn9ZtObPpzZvfU4DiYkiB/RcHG+S7fyxAMH/lFU2GOZd2bLx18/cEUMoD4j9I+DcS/RtJHGTYf4AAAwAxaOMYHjxKFwAAAABJRU5ErkJggg=='); background-position: 50% 50%; background-repeat: no-repeat; }
|
||||
.drawing-board-control-drawingmode-pencil-button:before { content: ""; display: block; width: 0; height: 100%; }
|
||||
|
||||
.drawing-board-control-drawingmode-eraser-button { overflow: hidden; *text-indent: -9999px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAkpJREFUeNp0kk+IElEcx39vFBc9+OfQRTAwzFt4CaYOKStj6MoeculStzoIQSB4kCVckmDx4iGCXWYJIqjoVOzO1l4qT1F7WfBWHvxzDPyTB3XUmXn93suRybUffHmP997n9/cRsFgwGARJkiAcDsPlwgEIeEZQAhCRAkgAlOD6SQP4rgMFDWVnYCAQgFgsBqFQCBwOByzZNQOotPHx1RNCCCipu6bfb+zSnslkeOQVILPrBkAirbws9btdTEWAzZPXpfepOzaeGMBXwe/3w3+MwTc3Dl+UeghTiskbBvR6Pbh18mZHB0jjmxvCKhIfR37s3r+Sevf8ca/T4TBF2HTSODuDxP7uNjrZFFbBk8lEzOVyspa4ykGYw2zfbTb/7ilvok1YhlVVFfP5vDydTkHXdXDdlhZOOnPY4/HA0YPtp3h6LFjh8XgsFgoFGTPgsKm1zDr8ajTQh8Fh5eGjZzjGI8yjKlgjF4tFGdd/YKYmRja24hw+zu3sYe2HiH3hYzQjl8tleTQanWtou93G6Qngdrth6+1+9h6hTULJZ/PeziJXKhV5OByeg1ut1gJOp9NZTdNOcQ419ot+ggp1qoLdBFmqVmNpm3A8Huewy+Wq1RH8QH9zmBlJJpMRdCIqiiIPBgN+2MCGsW/r8/kgGo1m0fmpzWarseayHlmNeL1eFiWC0cRqtSr3+/3FpSiKHMZtjU1glbFyfKgLTqfzEka9OJvNeDnzz1JnCaFmqOl8ZdJY1SiDOXCiXKg1NtG5DIt0y6ov3dE/AgwAENFWYYLj4mYAAAAASUVORK5CYII='); background-position: 50% 50%; background-repeat: no-repeat; }
|
||||
.drawing-board-control-drawingmode-eraser-button:before { content: ""; display: block; width: 0; height: 100%; }
|
||||
|
||||
.drawing-board-control-drawingmode-filler-button { overflow: hidden; *text-indent: -9999px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnNJREFUeNp0k0trE1EUx89MJpNJooYYXBgDNtCKdRPwlbqoCKUtaNVNA0Uo7UbMxoVPEARTXEi+QWfnwn6DEAlEkrSLttTGRiULEQlJ8yChmbzI++E50yTUJA78uMy953/u/557LmOz2WDEZ2m1WrckSRJSqdR2tVrdHQyYebwHtVoNuFHqTqczhQnWKaBYLDoKhcIuzgHDMKBSqeD20qd+LNdsNocSoFhRr9ctpVLJigl4xIIJQizLAmG4cAPa7bYcy9Iug5TL5UYikbD6/X7Rbre/IUcYe3WUW5ZsnQQzW9LpNOPz+UQc5aBM5mgdh7vI9FCCAesW2tnr9YqZTAby+bw8f3AQRP6853n+Ph5hemSCntjj8YjZbFYWx2IxeS2RSEMwuA87O79eqdXquVolK+GxnP0EPbHb7RZJSGABIR6PA11zJHKIR2MhHA5DIPDj7eH3j95KpfK60Wg8Yntil8slkqgnpioLghacTidoNDpEC3q9HnheCc3s1jZeLcW943pirPw/4lKpBkqlDubnl/riycnLsLy88EKj0fhzuRyZv8RFo1E6wpBYkiqy7Z54YmIcVlYeyOKC4mYwJ0nHRaQuM5vNT6hB/iceG7sIq6sPnwmC4MerDkby40AOCCoiddie1Wp92W7zQ2KTyQSLizNP8T0EsPLBbxEDnCj0GkM2qIEwyZRCobizsfH5A1ZXFhuN52F29vpz3HkL574mk8lj24Y5wsHkvjjoX0BOIWc5jruHzbK2ufmzEwpFO3jnDhQv4JoROYdoERVyGjEgZ8iBDlF3FzXo4go6utZ9lftY4N/dXisjR0i1G0ublv8KMAA0ZoUlicxrhwAAAABJRU5ErkJggg=='); background-position: 50% 50%; background-repeat: no-repeat; }
|
||||
.drawing-board-control-drawingmode-filler-button:before { content: ""; display: block; width: 0; height: 100%; }
|
||||
|
||||
.drawing-board-control-navigation > button { font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: bold; margin-right: 2px; }
|
||||
.drawing-board-control-navigation > button:last-child { margin-right: 0; }
|
||||
|
||||
.drawing-board-control-size[data-drawing-board-type="range"] .drawing-board-control-inner { width: 75px; }
|
||||
.drawing-board-control-size[data-drawing-board-type="dropdown"] .drawing-board-control-inner { overflow: visible; }
|
||||
|
||||
.drawing-board-control-size-range-input { position: relative; width: 100%; z-index: 100; margin: 0; padding: 0; border: 0; }
|
||||
|
||||
.drawing-board-control-size-range-current, .drawing-board-control-size-dropdown-current span, .drawing-board-control-size-dropdown span { display: block; background: #333; opacity: .8; }
|
||||
|
||||
.drawing-board-control-size-range-current { display: inline-block; opacity: .15; position: absolute; pointer-events: none; left: 50%; top: 50%; z-index: 50; }
|
||||
|
||||
.drawing-board-control-size-dropdown-current { display: block; height: 100%; width: 40px; overflow: hidden; position: relative; }
|
||||
.drawing-board-control-size-dropdown-current span { position: absolute; left: 50%; top: 50%; }
|
||||
|
||||
.drawing-board-control-size-dropdown { position: absolute; left: -6px; top: 33px; height: auto; list-style-type: none; margin: 0; padding: 0; z-index: 100; }
|
||||
.drawing-board-control-size-dropdown li { display: block; padding: 4px; margin: 3px 0; min-height: 16px; }
|
||||
.drawing-board-control-size-dropdown li:hover { background: #ccc; }
|
||||
.drawing-board-control-size-dropdown span { margin: 0 auto; }
|
||||
|
||||
.drawing-board-control-download-button { overflow: hidden; *text-indent: -9999px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoBJREFUeNqMkr9PU1EUx7/vR1tQ3yu10hAmTawtBSYSy2YccFOcnDQm/gewOLnj5GYMg4sdXFxkMca4OBAwdUBe/ZkIGFp+9tHXvh/3/fTcAm01aLzJybnn3nM+95tzrnDl6Tb+sibuTmWUWj3C6/Juk+LySUmyvt0FCKKA02ryOCy6LBiu15ngMbZ5DDCNBqIw6gKM+n4nECUJru3glKry8CpjQaHVYmC2rVH82DIMMMdGGARdwJ+SPNdFS9chx+MXDNMp/NzagWNatk/nQU/hiYAoih6FYTBCBs9zUXMCbAhx2OYOv351lPOJ3EwH4LteL6Dcp/Rfu3FrstDyIizt+agpaYxNDU0M9gl4v7Ck+TYrCYLQqZHUyTtdQBiutPSGUflczSXHs5lVKwZdSOBMvwztxVvN0RtzsiyXBFHsAvL5PBSnCpXV2getILFiE2SjspYbuZzPiDSZ2vOXmlvX5yQqTmMfg9ZXqtls1wnT09OHEyAq0aFLg/gSXsSWq9wWk+p9PrCoYTwcijdLOfE7UsEufN9HGIYnT4EnTGIXe1KqtNNIvuNnGamxfi7SgQD/nIJCTbzOPQ/SQh1pud7T4M6W/8qFIw/5WAr5m7Ozsw9UVc069Fls2yJzSC5/lnc9RhaHZVnfSqUnEgXP2oBqtYqBgYG2+mKxmOVADnAcB4yxHgD1RzehKKns/LyV4gUHBweQy+UyRkdH6UKJ6fQDFxcXoWkaXJeRuTgUGCdLQJ9bx72lGZimGWs2m+083oN+2iiFQiGxvLy8RrDzudyltgrG3N8U2G8CrPz4sGYYRqJSqWR4H/jNWbJhUjAWi8XG8R/L87yPpGCVttVfAgwAVpZR+8tZC08AAAAASUVORK5CYII='); background-position: 50% 50%; background-repeat: no-repeat; }
|
||||
.drawing-board-control-download-button:before { content: ""; display: block; width: 0; height: 100%; }
|
||||
+1374
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,16 @@
|
||||
/* drawingboard.js v0.4.6 - https://github.com/Leimi/drawingboard.js
|
||||
* Copyright (c) 2015 Emmanuel Pelletier
|
||||
* Licensed MIT */
|
||||
.drawing-board, .drawing-board * { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; }
|
||||
|
||||
.drawing-board-utils-hidden { display: none !important; }
|
||||
|
||||
.drawing-board { position: relative; display: block; }
|
||||
|
||||
.drawing-board-canvas-wrapper { position: relative; margin: 0; border: 1px solid #ddd; }
|
||||
|
||||
.drawing-board-canvas { position: absolute; top: 0; left: 0; z-index: 10; width: auto; }
|
||||
|
||||
.drawing-board-canvas { cursor: crosshair; z-index: 20; }
|
||||
|
||||
.drawing-board-cursor { position: absolute; top: 0; left: 0; pointer-events: none; border-radius: 50%; background: #ccc; background: rgba(0, 0, 0, 0.2); z-index: 30; }
|
||||
@@ -0,0 +1,971 @@
|
||||
/* drawingboard.js v0.4.6 - https://github.com/Leimi/drawingboard.js
|
||||
* Copyright (c) 2015 Emmanuel Pelletier
|
||||
* Licensed MIT */
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* SimpleUndo is a very basic javascript undo/redo stack for managing histories of basically anything.
|
||||
*
|
||||
* options are: {
|
||||
* * `provider` : required. a function to call on `save`, which should provide the current state of the historized object through the given "done" callback
|
||||
* * `maxLength` : the maximum number of items in history
|
||||
* * `opUpdate` : a function to call to notify of changes in history. Will be called on `save`, `undo`, `redo` and `clear`
|
||||
* }
|
||||
*
|
||||
*/
|
||||
var SimpleUndo = function(options) {
|
||||
|
||||
var settings = options ? options : {};
|
||||
var defaultOptions = {
|
||||
provider: function() {
|
||||
throw new Error("No provider!");
|
||||
},
|
||||
maxLength: 30,
|
||||
onUpdate: function() {}
|
||||
};
|
||||
|
||||
this.provider = (typeof settings.provider != 'undefined') ? settings.provider : defaultOptions.provider;
|
||||
this.maxLength = (typeof settings.maxLength != 'undefined') ? settings.maxLength : defaultOptions.maxLength;
|
||||
this.onUpdate = (typeof settings.onUpdate != 'undefined') ? settings.onUpdate : defaultOptions.onUpdate;
|
||||
|
||||
this.initialItem = null;
|
||||
this.clear();
|
||||
};
|
||||
|
||||
function truncate (stack, limit) {
|
||||
while (stack.length > limit) {
|
||||
stack.shift();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleUndo.prototype.initialize = function(initialItem) {
|
||||
this.stack[0] = initialItem;
|
||||
this.initialItem = initialItem;
|
||||
};
|
||||
|
||||
|
||||
SimpleUndo.prototype.clear = function() {
|
||||
this.stack = [this.initialItem];
|
||||
this.position = 0;
|
||||
this.onUpdate();
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.save = function() {
|
||||
this.provider(function(current) {
|
||||
truncate(this.stack, this.maxLength);
|
||||
this.position = Math.min(this.position,this.stack.length - 1);
|
||||
|
||||
this.stack = this.stack.slice(0, this.position + 1);
|
||||
this.stack.push(current);
|
||||
this.position++;
|
||||
this.onUpdate();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.undo = function(callback) {
|
||||
if (this.canUndo()) {
|
||||
var item = this.stack[--this.position];
|
||||
this.onUpdate();
|
||||
|
||||
if (callback) {
|
||||
callback(item);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.redo = function(callback) {
|
||||
if (this.canRedo()) {
|
||||
var item = this.stack[++this.position];
|
||||
this.onUpdate();
|
||||
|
||||
if (callback) {
|
||||
callback(item);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.canUndo = function() {
|
||||
return this.position > 0;
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.canRedo = function() {
|
||||
return this.position < this.count();
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.count = function() {
|
||||
return this.stack.length - 1; // -1 because of initial item
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//exports
|
||||
// node module
|
||||
if (typeof module != 'undefined') {
|
||||
module.exports = SimpleUndo;
|
||||
}
|
||||
|
||||
// browser global
|
||||
if (typeof window != 'undefined') {
|
||||
window.SimpleUndo = SimpleUndo;
|
||||
}
|
||||
|
||||
})();
|
||||
window.DrawingBoard = typeof DrawingBoard !== "undefined" ? DrawingBoard : {};
|
||||
|
||||
|
||||
DrawingBoard.Utils = {};
|
||||
|
||||
/*!
|
||||
* Tim (lite)
|
||||
* github.com/premasagar/tim
|
||||
*//*
|
||||
A tiny, secure JavaScript micro-templating script.
|
||||
*/
|
||||
DrawingBoard.Utils.tpl = (function(){
|
||||
"use strict";
|
||||
|
||||
var start = "{{",
|
||||
end = "}}",
|
||||
path = "[a-z0-9_][\\.a-z0-9_]*", // e.g. config.person.name
|
||||
pattern = new RegExp(start + "\\s*("+ path +")\\s*" + end, "gi"),
|
||||
undef;
|
||||
|
||||
return function(template, data){
|
||||
// Merge data into the template string
|
||||
return template.replace(pattern, function(tag, token){
|
||||
var path = token.split("."),
|
||||
len = path.length,
|
||||
lookup = data,
|
||||
i = 0;
|
||||
|
||||
for (; i < len; i++){
|
||||
lookup = lookup[path[i]];
|
||||
|
||||
// Property not found
|
||||
if (lookup === undef){
|
||||
throw "tim: '" + path[i] + "' not found in " + tag;
|
||||
}
|
||||
|
||||
// Return the required value
|
||||
if (i === len - 1){
|
||||
return lookup;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* https://github.com/jeromeetienne/microevent.js
|
||||
* MicroEvent - to make any js object an event emitter (server or browser)
|
||||
*
|
||||
* - pure javascript - server compatible, browser compatible
|
||||
* - dont rely on the browser doms
|
||||
* - super simple - you get it immediatly, no mistery, no magic involved
|
||||
*
|
||||
* - create a MicroEventDebug with goodies to debug
|
||||
* - make it safer to use
|
||||
*/
|
||||
DrawingBoard.Utils.MicroEvent = function(){};
|
||||
|
||||
DrawingBoard.Utils.MicroEvent.prototype = {
|
||||
bind : function(event, fct){
|
||||
this._events = this._events || {};
|
||||
this._events[event] = this._events[event] || [];
|
||||
this._events[event].push(fct);
|
||||
},
|
||||
unbind : function(event, fct){
|
||||
this._events = this._events || {};
|
||||
if( event in this._events === false ) return;
|
||||
this._events[event].splice(this._events[event].indexOf(fct), 1);
|
||||
},
|
||||
trigger : function(event /* , args... */){
|
||||
this._events = this._events || {};
|
||||
if( event in this._events === false ) return;
|
||||
for(var i = 0; i < this._events[event].length; i++){
|
||||
this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//I know.
|
||||
DrawingBoard.Utils._boxBorderSize = function($el, withPadding, withMargin, direction) {
|
||||
withPadding = !!withPadding || true;
|
||||
withMargin = !!withMargin || false;
|
||||
var width = 0,
|
||||
props;
|
||||
if (direction == "width") {
|
||||
props = ['border-left-width', 'border-right-width'];
|
||||
if (withPadding) props.push('padding-left', 'padding-right');
|
||||
if (withMargin) props.push('margin-left', 'margin-right');
|
||||
} else {
|
||||
props = ['border-top-width', 'border-bottom-width'];
|
||||
if (withPadding) props.push('padding-top', 'padding-bottom');
|
||||
if (withMargin) props.push('margin-top', 'margin-bottom');
|
||||
}
|
||||
for (var i = props.length - 1; i >= 0; i--)
|
||||
width += parseInt($el.css(props[i]).replace('px', ''), 10);
|
||||
return width;
|
||||
};
|
||||
|
||||
DrawingBoard.Utils.boxBorderWidth = function($el, withPadding, withMargin) {
|
||||
return DrawingBoard.Utils._boxBorderSize($el, withPadding, withMargin, 'width');
|
||||
};
|
||||
|
||||
DrawingBoard.Utils.boxBorderHeight = function($el, withPadding, withMargin) {
|
||||
return DrawingBoard.Utils._boxBorderSize($el, withPadding, withMargin, 'height');
|
||||
};
|
||||
|
||||
DrawingBoard.Utils.isColor = function(string) {
|
||||
if (!string || !string.length) return false;
|
||||
return (/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i).test(string) || $.inArray(string.substring(0, 3), ['rgb', 'hsl']) !== -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Packs an RGB color into a single integer.
|
||||
*/
|
||||
DrawingBoard.Utils.RGBToInt = function(r, g, b) {
|
||||
var c = 0;
|
||||
c |= (r & 255) << 16;
|
||||
c |= (g & 255) << 8;
|
||||
c |= (b & 255);
|
||||
return c;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns informations on the pixel located at (x,y).
|
||||
*/
|
||||
DrawingBoard.Utils.pixelAt = function(image, x, y) {
|
||||
var i = (y * image.width + x) * 4;
|
||||
var c = DrawingBoard.Utils.RGBToInt(
|
||||
image.data[i],
|
||||
image.data[i + 1],
|
||||
image.data[i + 2]
|
||||
);
|
||||
|
||||
return [
|
||||
i, // INDEX
|
||||
x, // X
|
||||
y, // Y
|
||||
c // COLOR
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares two colors with the given tolerance (between 0 and 255).
|
||||
*/
|
||||
DrawingBoard.Utils.compareColors = function(a, b, tolerance) {
|
||||
if (tolerance === 0) {
|
||||
return (a === b);
|
||||
}
|
||||
|
||||
var ra = (a >> 16) & 255, rb = (b >> 16) & 255,
|
||||
ga = (a >> 8) & 255, gb = (b >> 8) & 255,
|
||||
ba = a & 255, bb = b & 255;
|
||||
|
||||
return (Math.abs(ra - rb) <= tolerance)
|
||||
&& (Math.abs(ga - gb) <= tolerance)
|
||||
&& (Math.abs(ba - bb) <= tolerance);
|
||||
};
|
||||
|
||||
(function() {
|
||||
var lastTime = 0;
|
||||
var vendors = ['ms', 'moz', 'webkit', 'o'];
|
||||
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
|
||||
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
|
||||
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
|
||||
}
|
||||
}());
|
||||
|
||||
window.DrawingBoard = typeof DrawingBoard !== "undefined" ? DrawingBoard : {};
|
||||
|
||||
/**
|
||||
* pass the id of the html element to put the drawing board into
|
||||
* and some options : {
|
||||
* controls: array of controls to initialize with the drawingboard. 'Colors', 'Size', and 'Navigation' by default
|
||||
* instead of simple strings, you can pass an object to define a control opts
|
||||
* ie ['Color', { Navigation: { reset: false }}]
|
||||
* controlsPosition: "top left" by default. Define where to put the controls: at the "top" or "bottom" of the canvas, aligned to "left"/"right"/"center"
|
||||
* background: background of the drawing board. Give a hex color or an image url "#ffffff" (white) by default
|
||||
* color: pencil color ("#000000" by default)
|
||||
* size: pencil size (3 by default)
|
||||
* webStorage: 'session', 'local' or false ('session' by default). store the current drawing in session or local storage and restore it when you come back
|
||||
* droppable: true or false (false by default). If true, dropping an image on the canvas will include it and allow you to draw on it,
|
||||
* errorMessage: html string to put in the board's element on browsers that don't support canvas.
|
||||
* stretchImg: default behavior of image setting on the canvas: set to the canvas width/height or not? false by default
|
||||
* }
|
||||
*/
|
||||
DrawingBoard.Board = function(id, opts) {
|
||||
this.opts = this.mergeOptions(opts);
|
||||
|
||||
this.ev = new DrawingBoard.Utils.MicroEvent();
|
||||
|
||||
this.id = id;
|
||||
this.$el = $(document.getElementById(id));
|
||||
if (!this.$el.length)
|
||||
return false;
|
||||
|
||||
var tpl = '<div class="drawing-board-canvas-wrapper"></canvas><canvas class="drawing-board-canvas"></canvas><div class="drawing-board-cursor drawing-board-utils-hidden"></div></div>';
|
||||
if (this.opts.controlsPosition.indexOf("bottom") > -1) tpl += '<div class="drawing-board-controls"></div>';
|
||||
else tpl = '<div class="drawing-board-controls"></div>' + tpl;
|
||||
|
||||
this.$el.addClass('drawing-board').append(tpl);
|
||||
this.dom = {
|
||||
$canvasWrapper: this.$el.find('.drawing-board-canvas-wrapper'),
|
||||
$canvas: this.$el.find('.drawing-board-canvas'),
|
||||
$cursor: this.$el.find('.drawing-board-cursor'),
|
||||
$controls: this.$el.find('.drawing-board-controls')
|
||||
};
|
||||
|
||||
$.each(['left', 'right', 'center'], $.proxy(function(n, val) {
|
||||
if (this.opts.controlsPosition.indexOf(val) > -1) {
|
||||
this.dom.$controls.attr('data-align', val);
|
||||
return false;
|
||||
}
|
||||
}, this));
|
||||
|
||||
this.canvas = this.dom.$canvas.get(0);
|
||||
this.ctx = this.canvas && this.canvas.getContext && this.canvas.getContext('2d') ? this.canvas.getContext('2d') : null;
|
||||
this.color = this.opts.color;
|
||||
|
||||
if (!this.ctx) {
|
||||
if (this.opts.errorMessage)
|
||||
this.$el.html(this.opts.errorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.storage = this._getStorage();
|
||||
|
||||
this.initHistory();
|
||||
//init default board values before controls are added (mostly pencil color and size)
|
||||
this.reset({ webStorage: false, history: false, background: false });
|
||||
//init controls (they will need the default board values to work like pencil color and size)
|
||||
this.initControls();
|
||||
//set board's size after the controls div is added
|
||||
this.resize();
|
||||
//reset the board to take all resized space
|
||||
this.reset({ webStorage: false, history: false, background: true });
|
||||
this.restoreWebStorage();
|
||||
this.initDropEvents();
|
||||
this.initDrawEvents();
|
||||
};
|
||||
|
||||
|
||||
|
||||
DrawingBoard.Board.defaultOpts = {
|
||||
controls: ['Color', 'DrawingMode', 'Size', 'Navigation'],
|
||||
controlsPosition: "top left",
|
||||
color: "#000000",
|
||||
size: 1,
|
||||
background: "#fff",
|
||||
eraserColor: "background",
|
||||
fillTolerance: 100,
|
||||
fillHack: true, //try to prevent issues with anti-aliasing with a little hack by default
|
||||
webStorage: 'session',
|
||||
droppable: false,
|
||||
enlargeYourContainer: false,
|
||||
errorMessage: "<p>It seems you use an obsolete browser. <a href=\"http://browsehappy.com/\" target=\"_blank\">Update it</a> to start drawing.</p>",
|
||||
stretchImg: false //when setting the canvas img, strech the image at the whole canvas size when this opt is true
|
||||
};
|
||||
|
||||
|
||||
|
||||
DrawingBoard.Board.prototype = {
|
||||
|
||||
mergeOptions: function(opts) {
|
||||
opts = $.extend({}, DrawingBoard.Board.defaultOpts, opts);
|
||||
if (!opts.background && opts.eraserColor === "background")
|
||||
opts.eraserColor = "transparent";
|
||||
return opts;
|
||||
},
|
||||
|
||||
/**
|
||||
* Canvas reset/resize methods: put back the canvas to its default values
|
||||
*
|
||||
* depending on options, can set color, size, background back to default values
|
||||
* and store the reseted canvas in webstorage and history queue
|
||||
*
|
||||
* resize values depend on the `enlargeYourContainer` option
|
||||
*/
|
||||
|
||||
reset: function(opts) {
|
||||
opts = $.extend({
|
||||
color: this.opts.color,
|
||||
size: this.opts.size,
|
||||
webStorage: true,
|
||||
history: true,
|
||||
background: false
|
||||
}, opts);
|
||||
|
||||
this.setMode('pencil');
|
||||
|
||||
if (opts.background) {
|
||||
this.resetBackground(this.opts.background, $.proxy(function() {
|
||||
if (opts.history) this.saveHistory();
|
||||
}, this));
|
||||
}
|
||||
|
||||
if (opts.color) this.setColor(opts.color);
|
||||
if (opts.size) this.ctx.lineWidth = opts.size;
|
||||
|
||||
this.ctx.lineCap = "round";
|
||||
this.ctx.lineJoin = "round";
|
||||
// this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.width);
|
||||
|
||||
if (opts.webStorage) this.saveWebStorage();
|
||||
|
||||
// if opts.background we already dealt with the history
|
||||
if (opts.history && !opts.background) this.saveHistory();
|
||||
|
||||
this.blankCanvas = this.getImg();
|
||||
|
||||
this.ev.trigger('board:reset', opts);
|
||||
},
|
||||
|
||||
resetBackground: function(background, callback) {
|
||||
background = background || this.opts.background;
|
||||
|
||||
var bgIsColor = DrawingBoard.Utils.isColor(background);
|
||||
var prevMode = this.getMode();
|
||||
this.setMode('pencil');
|
||||
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
||||
if (bgIsColor) {
|
||||
this.ctx.fillStyle = background;
|
||||
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
||||
this.history.initialize(this.getImg());
|
||||
if (callback) callback();
|
||||
} else if (background)
|
||||
this.setImg(background, {
|
||||
callback: $.proxy(function() {
|
||||
this.history.initialize(this.getImg());
|
||||
if (callback) callback();
|
||||
}, this)
|
||||
});
|
||||
this.setMode(prevMode);
|
||||
},
|
||||
|
||||
resize: function() {
|
||||
this.dom.$controls.toggleClass('drawing-board-controls-hidden', (!this.controls || !this.controls.length));
|
||||
|
||||
var canvasWidth, canvasHeight;
|
||||
var widths = [
|
||||
this.$el.width(),
|
||||
DrawingBoard.Utils.boxBorderWidth(this.$el),
|
||||
DrawingBoard.Utils.boxBorderWidth(this.dom.$canvasWrapper, true, true)
|
||||
];
|
||||
var heights = [
|
||||
this.$el.height(),
|
||||
DrawingBoard.Utils.boxBorderHeight(this.$el),
|
||||
this.dom.$controls.height(),
|
||||
DrawingBoard.Utils.boxBorderHeight(this.dom.$controls, false, true),
|
||||
DrawingBoard.Utils.boxBorderHeight(this.dom.$canvasWrapper, true, true)
|
||||
];
|
||||
var that = this;
|
||||
var sum = function(values, multiplier) { //make the sum of all array values
|
||||
multiplier = multiplier || 1;
|
||||
var res = values[0];
|
||||
for (var i = 1; i < values.length; i++) {
|
||||
res = res + (values[i]*multiplier);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
var sub = function(values) { return sum(values, -1); }; //substract all array values from the first one
|
||||
|
||||
if (this.opts.enlargeYourContainer) {
|
||||
canvasWidth = this.$el.width();
|
||||
canvasHeight = this.$el.height();
|
||||
|
||||
this.$el.width( sum(widths) );
|
||||
this.$el.height( sum(heights) );
|
||||
} else {
|
||||
canvasWidth = sub(widths);
|
||||
canvasHeight = sub(heights);
|
||||
}
|
||||
|
||||
this.dom.$canvasWrapper.css('width', canvasWidth + 'px');
|
||||
this.dom.$canvasWrapper.css('height', canvasHeight + 'px');
|
||||
|
||||
this.dom.$canvas.css('width', canvasWidth + 'px');
|
||||
this.dom.$canvas.css('height', canvasHeight + 'px');
|
||||
|
||||
this.canvas.width = canvasWidth;
|
||||
this.canvas.height = canvasHeight;
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Controls:
|
||||
* the drawing board can has various UI elements to control it.
|
||||
* one control is represented by a class in the namespace DrawingBoard.Control
|
||||
* it must have a $el property (jQuery object), representing the html element to append on the drawing board at initialization.
|
||||
*
|
||||
*/
|
||||
|
||||
initControls: function() {
|
||||
this.controls = [];
|
||||
if (!this.opts.controls.length || !DrawingBoard.Control) return false;
|
||||
for (var i = 0; i < this.opts.controls.length; i++) {
|
||||
var c = null;
|
||||
if (typeof this.opts.controls[i] == "string")
|
||||
c = new window['DrawingBoard']['Control'][this.opts.controls[i]](this);
|
||||
else if (typeof this.opts.controls[i] == "object") {
|
||||
for (var controlName in this.opts.controls[i]) break;
|
||||
c = new window['DrawingBoard']['Control'][controlName](this, this.opts.controls[i][controlName]);
|
||||
}
|
||||
if (c) {
|
||||
this.addControl(c);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//add a new control or an existing one at the position you want in the UI
|
||||
//to add a totally new control, you can pass a string with the js class as 1st parameter and control options as 2nd ie "addControl('Navigation', { reset: false }"
|
||||
//the last parameter (2nd or 3rd depending on the situation) is always the position you want to place the control at
|
||||
addControl: function(control, optsOrPos, pos) {
|
||||
if (typeof control !== "string" && (typeof control !== "object" || !control instanceof DrawingBoard.Control))
|
||||
return false;
|
||||
|
||||
var opts = typeof optsOrPos == "object" ? optsOrPos : {};
|
||||
pos = pos ? pos*1 : (typeof optsOrPos == "number" ? optsOrPos : null);
|
||||
|
||||
if (typeof control == "string")
|
||||
control = new window['DrawingBoard']['Control'][control](this, opts);
|
||||
|
||||
if (pos)
|
||||
this.dom.$controls.children().eq(pos).before(control.$el);
|
||||
else
|
||||
this.dom.$controls.append(control.$el);
|
||||
|
||||
if (!this.controls)
|
||||
this.controls = [];
|
||||
this.controls.push(control);
|
||||
this.dom.$controls.removeClass('drawing-board-controls-hidden');
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* History methods: undo and redo drawed lines
|
||||
*/
|
||||
|
||||
initHistory: function() {
|
||||
this.history = new SimpleUndo({
|
||||
maxLength: 30,
|
||||
provider: $.proxy(function(done) {
|
||||
done(this.getImg());
|
||||
}, this),
|
||||
onUpdate: $.proxy(function() {
|
||||
this.ev.trigger('historyNavigation');
|
||||
}, this)
|
||||
});
|
||||
},
|
||||
|
||||
saveHistory: function() {
|
||||
this.history.save();
|
||||
},
|
||||
|
||||
restoreHistory: function(image) {
|
||||
this.setImg(image, {
|
||||
callback: $.proxy(function() {
|
||||
this.saveWebStorage();
|
||||
}, this)
|
||||
});
|
||||
},
|
||||
|
||||
goBackInHistory: function() {
|
||||
this.history.undo($.proxy(this.restoreHistory, this));
|
||||
},
|
||||
|
||||
goForthInHistory: function() {
|
||||
this.history.redo($.proxy(this.restoreHistory, this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Image methods: you can directly put an image on the canvas, get it in base64 data url or start a download
|
||||
*/
|
||||
|
||||
setImg: function(src, opts) {
|
||||
opts = $.extend({
|
||||
stretch: this.opts.stretchImg,
|
||||
callback: null
|
||||
}, opts);
|
||||
|
||||
var ctx = this.ctx;
|
||||
var img = new Image();
|
||||
var oldGCO = ctx.globalCompositeOperation;
|
||||
img.onload = function() {
|
||||
ctx.globalCompositeOperation = "source-over";
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
|
||||
if (opts.stretch) {
|
||||
ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
} else {
|
||||
ctx.drawImage(img, 0, 0);
|
||||
}
|
||||
|
||||
ctx.globalCompositeOperation = oldGCO;
|
||||
|
||||
if (opts.callback) {
|
||||
opts.callback();
|
||||
}
|
||||
};
|
||||
img.src = src;
|
||||
},
|
||||
|
||||
getImg: function() {
|
||||
return this.canvas.toDataURL("image/png");
|
||||
},
|
||||
|
||||
downloadImg: function() {
|
||||
var img = this.getImg();
|
||||
img = img.replace("image/png", "image/octet-stream");
|
||||
window.location.href = img;
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* WebStorage handling : save and restore to local or session storage
|
||||
*/
|
||||
|
||||
saveWebStorage: function() {
|
||||
if (window[this.storage]) {
|
||||
window[this.storage].setItem('drawing-board-' + this.id, this.getImg());
|
||||
this.ev.trigger('board:save' + this.storage.charAt(0).toUpperCase() + this.storage.slice(1), this.getImg());
|
||||
}
|
||||
},
|
||||
|
||||
restoreWebStorage: function() {
|
||||
if (window[this.storage] && window[this.storage].getItem('drawing-board-' + this.id) !== null) {
|
||||
this.setImg(window[this.storage].getItem('drawing-board-' + this.id));
|
||||
this.ev.trigger('board:restore' + this.storage.charAt(0).toUpperCase() + this.storage.slice(1), window[this.storage].getItem('drawing-board-' + this.id));
|
||||
}
|
||||
},
|
||||
|
||||
clearWebStorage: function() {
|
||||
if (window[this.storage] && window[this.storage].getItem('drawing-board-' + this.id) !== null) {
|
||||
window[this.storage].removeItem('drawing-board-' + this.id);
|
||||
this.ev.trigger('board:clear' + this.storage.charAt(0).toUpperCase() + this.storage.slice(1));
|
||||
}
|
||||
},
|
||||
|
||||
_getStorage: function() {
|
||||
if (!this.opts.webStorage || !(this.opts.webStorage === 'session' || this.opts.webStorage === 'local')) return false;
|
||||
return this.opts.webStorage + 'Storage';
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Drop an image on the canvas to draw on it
|
||||
*/
|
||||
|
||||
initDropEvents: function() {
|
||||
if (!this.opts.droppable)
|
||||
return false;
|
||||
|
||||
this.dom.$canvas.on('dragover dragenter drop', function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
this.dom.$canvas.on('drop', $.proxy(this._onCanvasDrop, this));
|
||||
},
|
||||
|
||||
_onCanvasDrop: function(e) {
|
||||
e = e.originalEvent ? e.originalEvent : e;
|
||||
var files = e.dataTransfer.files;
|
||||
if (!files || !files.length || files[0].type.indexOf('image') == -1 || !window.FileReader)
|
||||
return false;
|
||||
var fr = new FileReader();
|
||||
fr.readAsDataURL(files[0]);
|
||||
fr.onload = $.proxy(function(ev) {
|
||||
this.setImg(ev.target.result, {
|
||||
callback: $.proxy(function() {
|
||||
this.saveHistory();
|
||||
}, this)
|
||||
});
|
||||
this.ev.trigger('board:imageDropped', ev.target.result);
|
||||
this.ev.trigger('board:userAction');
|
||||
}, this);
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* set and get current drawing mode
|
||||
*
|
||||
* possible modes are "pencil" (draw normally), "eraser" (draw transparent, like, erase, you know), "filler" (paint can)
|
||||
*/
|
||||
|
||||
setMode: function(newMode, silent) {
|
||||
silent = silent || false;
|
||||
newMode = newMode || 'pencil';
|
||||
|
||||
this.ev.unbind('board:startDrawing', $.proxy(this.fill, this));
|
||||
|
||||
if (this.opts.eraserColor === "transparent")
|
||||
this.ctx.globalCompositeOperation = newMode === "eraser" ? "destination-out" : "source-over";
|
||||
else {
|
||||
if (newMode === "eraser") {
|
||||
if (this.opts.eraserColor === "background" && DrawingBoard.Utils.isColor(this.opts.background))
|
||||
this.ctx.strokeStyle = this.opts.background;
|
||||
else if (DrawingBoard.Utils.isColor(this.opts.eraserColor))
|
||||
this.ctx.strokeStyle = this.opts.eraserColor;
|
||||
} else if (!this.mode || this.mode === "eraser") {
|
||||
this.ctx.strokeStyle = this.color;
|
||||
}
|
||||
|
||||
if (newMode === "filler")
|
||||
this.ev.bind('board:startDrawing', $.proxy(this.fill, this));
|
||||
}
|
||||
this.mode = newMode;
|
||||
if (!silent)
|
||||
this.ev.trigger('board:mode', this.mode);
|
||||
},
|
||||
|
||||
getMode: function() {
|
||||
return this.mode || "pencil";
|
||||
},
|
||||
|
||||
setColor: function(color) {
|
||||
var that = this;
|
||||
color = color || this.color;
|
||||
if (!DrawingBoard.Utils.isColor(color))
|
||||
return false;
|
||||
this.color = color;
|
||||
if (this.opts.eraserColor !== "transparent" && this.mode === "eraser") {
|
||||
var setStrokeStyle = function(mode) {
|
||||
if (mode !== "eraser")
|
||||
that.strokeStyle = that.color;
|
||||
that.ev.unbind('board:mode', setStrokeStyle);
|
||||
};
|
||||
this.ev.bind('board:mode', setStrokeStyle);
|
||||
} else
|
||||
this.ctx.strokeStyle = this.color;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fills an area with the current stroke color.
|
||||
*/
|
||||
fill: function(e) {
|
||||
if (this.getImg() === this.blankCanvas) {
|
||||
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
||||
this.ctx.fillStyle = this.color;
|
||||
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
||||
return;
|
||||
}
|
||||
|
||||
var img = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
// constants identifying pixels components
|
||||
var INDEX = 0, X = 1, Y = 2, COLOR = 3;
|
||||
|
||||
// target color components
|
||||
var stroke = this.ctx.strokeStyle;
|
||||
var r = parseInt(stroke.substr(1, 2), 16);
|
||||
var g = parseInt(stroke.substr(3, 2), 16);
|
||||
var b = parseInt(stroke.substr(5, 2), 16);
|
||||
|
||||
// starting point
|
||||
var start = DrawingBoard.Utils.pixelAt(img, parseInt(e.coords.x, 10), parseInt(e.coords.y, 10));
|
||||
var startColor = start[COLOR];
|
||||
var tolerance = this.opts.fillTolerance;
|
||||
var useHack = this.opts.fillHack; //see https://github.com/Leimi/drawingboard.js/pull/38
|
||||
|
||||
// no need to continue if starting and target colors are the same
|
||||
if (DrawingBoard.Utils.compareColors(startColor, DrawingBoard.Utils.RGBToInt(r, g, b), tolerance))
|
||||
return;
|
||||
|
||||
// pixels to evaluate
|
||||
var queue = [start];
|
||||
|
||||
// loop vars
|
||||
var pixel, x, y;
|
||||
var maxX = img.width - 1;
|
||||
var maxY = img.height - 1;
|
||||
|
||||
function updatePixelColor(pixel) {
|
||||
img.data[pixel[INDEX]] = r;
|
||||
img.data[pixel[INDEX] + 1] = g;
|
||||
img.data[pixel[INDEX] + 2] = b;
|
||||
}
|
||||
|
||||
while ((pixel = queue.pop())) {
|
||||
if (useHack)
|
||||
updatePixelColor(pixel);
|
||||
|
||||
if (DrawingBoard.Utils.compareColors(pixel[COLOR], startColor, tolerance)) {
|
||||
if (!useHack)
|
||||
updatePixelColor(pixel);
|
||||
if (pixel[X] > 0) // west
|
||||
queue.push(DrawingBoard.Utils.pixelAt(img, pixel[X] - 1, pixel[Y]));
|
||||
if (pixel[X] < maxX) // east
|
||||
queue.push(DrawingBoard.Utils.pixelAt(img, pixel[X] + 1, pixel[Y]));
|
||||
if (pixel[Y] > 0) // north
|
||||
queue.push(DrawingBoard.Utils.pixelAt(img, pixel[X], pixel[Y] - 1));
|
||||
if (pixel[Y] < maxY) // south
|
||||
queue.push(DrawingBoard.Utils.pixelAt(img, pixel[X], pixel[Y] + 1));
|
||||
}
|
||||
}
|
||||
|
||||
this.ctx.putImageData(img, 0, 0);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Drawing handling, with mouse or touch
|
||||
*/
|
||||
|
||||
initDrawEvents: function() {
|
||||
this.isDrawing = false;
|
||||
this.isMouseHovering = false;
|
||||
this.coords = {};
|
||||
this.coords.old = this.coords.current = this.coords.oldMid = { x: 0, y: 0 };
|
||||
|
||||
this.dom.$canvas.on('mousedown touchstart', $.proxy(function(e) {
|
||||
this._onInputStart(e, this._getInputCoords(e) );
|
||||
}, this));
|
||||
|
||||
this.dom.$canvas.on('mousemove touchmove', $.proxy(function(e) {
|
||||
this._onInputMove(e, this._getInputCoords(e) );
|
||||
}, this));
|
||||
|
||||
this.dom.$canvas.on('mousemove', $.proxy(function(e) {
|
||||
|
||||
}, this));
|
||||
|
||||
this.dom.$canvas.on('mouseup touchend', $.proxy(function(e) {
|
||||
this._onInputStop(e, this._getInputCoords(e) );
|
||||
}, this));
|
||||
|
||||
this.dom.$canvas.on('mouseover', $.proxy(function(e) {
|
||||
this._onMouseOver(e, this._getInputCoords(e) );
|
||||
}, this));
|
||||
|
||||
this.dom.$canvas.on('mouseout', $.proxy(function(e) {
|
||||
this._onMouseOut(e, this._getInputCoords(e) );
|
||||
|
||||
}, this));
|
||||
|
||||
$('body').on('mouseup touchend', $.proxy(function(e) {
|
||||
this.isDrawing = false;
|
||||
}, this));
|
||||
|
||||
if (window.requestAnimationFrame) requestAnimationFrame( $.proxy(this.draw, this) );
|
||||
},
|
||||
|
||||
draw: function() {
|
||||
//if the pencil size is big (>10), the small crosshair makes a friend: a circle of the size of the pencil
|
||||
//todo: have the circle works on every browser - it currently should be added only when CSS pointer-events are supported
|
||||
//we assume that if requestAnimationFrame is supported, pointer-events is too, but this is terribad.
|
||||
if (window.requestAnimationFrame && this.ctx.lineWidth > 10 && this.isMouseHovering) {
|
||||
this.dom.$cursor.css({ width: this.ctx.lineWidth + 'px', height: this.ctx.lineWidth + 'px' });
|
||||
var transform = DrawingBoard.Utils.tpl("translateX({{x}}px) translateY({{y}}px)", { x: this.coords.current.x-(this.ctx.lineWidth/2), y: this.coords.current.y-(this.ctx.lineWidth/2) });
|
||||
this.dom.$cursor.css({ 'transform': transform, '-webkit-transform': transform, '-ms-transform': transform });
|
||||
this.dom.$cursor.removeClass('drawing-board-utils-hidden');
|
||||
} else {
|
||||
this.dom.$cursor.addClass('drawing-board-utils-hidden');
|
||||
}
|
||||
|
||||
if (this.isDrawing) {
|
||||
var currentMid = this._getMidInputCoords(this.coords.current);
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(currentMid.x, currentMid.y);
|
||||
this.ctx.quadraticCurveTo(this.coords.old.x, this.coords.old.y, this.coords.oldMid.x, this.coords.oldMid.y);
|
||||
this.ctx.stroke();
|
||||
|
||||
this.coords.old = this.coords.current;
|
||||
this.coords.oldMid = currentMid;
|
||||
}
|
||||
|
||||
if (window.requestAnimationFrame) requestAnimationFrame( $.proxy(function() { this.draw(); }, this) );
|
||||
},
|
||||
|
||||
_onInputStart: function(e, coords) {
|
||||
this.coords.current = this.coords.old = coords;
|
||||
this.coords.oldMid = this._getMidInputCoords(coords);
|
||||
this.isDrawing = true;
|
||||
|
||||
if (!window.requestAnimationFrame) this.draw();
|
||||
|
||||
this.ev.trigger('board:startDrawing', {e: e, coords: coords});
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
_onInputMove: function(e, coords) {
|
||||
this.coords.current = coords;
|
||||
this.ev.trigger('board:drawing', {e: e, coords: coords});
|
||||
|
||||
if (!window.requestAnimationFrame) this.draw();
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
_onInputStop: function(e, coords) {
|
||||
if (this.isDrawing && (!e.touches || e.touches.length === 0)) {
|
||||
this.isDrawing = false;
|
||||
|
||||
this.saveWebStorage();
|
||||
this.saveHistory();
|
||||
|
||||
this.ev.trigger('board:stopDrawing', {e: e, coords: coords});
|
||||
this.ev.trigger('board:userAction');
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
_onMouseOver: function(e, coords) {
|
||||
this.isMouseHovering = true;
|
||||
this.coords.old = this._getInputCoords(e);
|
||||
this.coords.oldMid = this._getMidInputCoords(this.coords.old);
|
||||
|
||||
this.ev.trigger('board:mouseOver', {e: e, coords: coords});
|
||||
},
|
||||
|
||||
_onMouseOut: function(e, coords) {
|
||||
this.isMouseHovering = false;
|
||||
|
||||
this.ev.trigger('board:mouseOut', {e: e, coords: coords});
|
||||
},
|
||||
|
||||
_getInputCoords: function(e) {
|
||||
e = e.originalEvent ? e.originalEvent : e;
|
||||
var
|
||||
rect = this.canvas.getBoundingClientRect(),
|
||||
width = this.dom.$canvas.width(),
|
||||
height = this.dom.$canvas.height()
|
||||
;
|
||||
var x, y;
|
||||
if (e.touches && e.touches.length == 1) {
|
||||
x = e.touches[0].pageX;
|
||||
y = e.touches[0].pageY;
|
||||
} else {
|
||||
x = e.pageX;
|
||||
y = e.pageY;
|
||||
}
|
||||
x = x - this.dom.$canvas.offset().left;
|
||||
y = y - this.dom.$canvas.offset().top;
|
||||
x *= (width / rect.width);
|
||||
y *= (height / rect.height);
|
||||
return {
|
||||
x: x,
|
||||
y: y
|
||||
};
|
||||
},
|
||||
|
||||
_getMidInputCoords: function(coords) {
|
||||
return {
|
||||
x: this.coords.old.x + coords.x>>1,
|
||||
y: this.coords.old.y + coords.y>>1
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
/* drawingboard.js v0.4.6 - https://github.com/Leimi/drawingboard.js
|
||||
* Copyright (c) 2015 Emmanuel Pelletier
|
||||
* Licensed MIT */
|
||||
|
||||
.drawing-board,.drawing-board *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.drawing-board-utils-hidden{display:none!important}.drawing-board{position:relative;display:block}.drawing-board-canvas-wrapper{position:relative;margin:0;border:1px solid #ddd}.drawing-board-canvas{position:absolute;top:0;left:0;width:auto;cursor:crosshair;z-index:20}.drawing-board-cursor{position:absolute;top:0;left:0;pointer-events:none;border-radius:50%;background:#ccc;background:rgba(0,0,0,.2);z-index:30}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,28 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<div style="row">
|
||||
<div id="dessin"></div>
|
||||
</div> <!-- row -->
|
||||
|
||||
|
||||
<!-- Drawingboard -->
|
||||
<link href="${request.static_url('mondumas:static/dist/drawingboard/drawingboard.min.css')}" rel="stylesheet" media="all">
|
||||
<script src="${request.static_url('mondumas:static/dist/drawingboard/drawingboard.min.js')}"></script>
|
||||
<script>
|
||||
var simpleBoard = new DrawingBoard.Board('dessin', {
|
||||
background: 'none',
|
||||
size: 10,
|
||||
controls: [
|
||||
'Color',
|
||||
{ Size: { type: 'dropdown' } },
|
||||
{ Navigation: { } },
|
||||
],
|
||||
size: 1,
|
||||
webStorage: 'session',
|
||||
enlargeYourContainer: true
|
||||
});
|
||||
</script>
|
||||
|
||||
</div><!-- content -->
|
||||
</metal:block>
|
||||
@@ -284,22 +284,37 @@
|
||||
</table>
|
||||
|
||||
<h3 class="text-center">NOTES et CROQUIS</h3>
|
||||
<p>
|
||||
<a class="btn btn-success" role="button" href="#"
|
||||
data-toggle="modal" data-target="#addNote"><span class="glyphicon glyphicon-plus"></span> Notes / Croquis</a>
|
||||
</p>
|
||||
<br />
|
||||
<div class="text-center" tal:repeat="item dem_notes">
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/note_edit/${nodossier}/${item.noligne}" tal:condition="item.type_note=='NOTE'">
|
||||
<span class="glyphicon glyphicon-text-size logo-primary"></span>
|
||||
<h4>${item.libelle}</h4></a>
|
||||
<a href="${request.application_url}/croquis_edit/${nodossier}/${item.noligne}" tal:condition="item.type_note=='CROQUIS'">
|
||||
<span class="glyphicon glyphicon-picture logo-primary"></span>
|
||||
<h4>${item.libelle}</h4></a>
|
||||
<div class="row">
|
||||
<!-- bouton AJOUT NOTE-->
|
||||
<div class="text-center col-sm-3">
|
||||
<a href="${request.application_url}/note_add/${nodossier}">
|
||||
<span class="glyphicon glyphicon-plus logo-success"></span>
|
||||
<h4>NOTE</h4></a>
|
||||
</div>
|
||||
<!-- Listes des Notes -->
|
||||
<div class="text-center" tal:repeat="item dem_notes">
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/note_edit/${nodossier}/${item.noligne}" tal:condition="item.type_note=='NOTE'">
|
||||
<span class="glyphicon glyphicon-text-size logo-primary"></span>
|
||||
<h4>${item.libelle}</h4></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="text-center col-sm-3">
|
||||
<a href="${request.application_url}/croquis_edit/">
|
||||
<span class="glyphicon glyphicon-plus logo-success"></span>
|
||||
<h4>CROQUIS</h4></a>
|
||||
</div>
|
||||
<div class="text-center" tal:repeat="item dem_croquis">
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/croquis_edit/" tal:condition="item.type_note=='CROQUIS'">
|
||||
<span class="glyphicon glyphicon-picture logo-primary"></span>
|
||||
<h4>${item.libelle}</h4></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- PANEL RDF -->
|
||||
<div id="tab_rdf" class="tab-pane fade">
|
||||
|
||||
@@ -129,7 +129,9 @@ def dossier_view(request):
|
||||
status = get_status_by_id(request, '')
|
||||
motifs = get_motifs(request)
|
||||
# lire les notes du dossier
|
||||
dem_notes = get_dem_notes(request, nodossier, '0')
|
||||
dem_notes = get_dem_notes(request, nodossier, '0', 'NOTE')
|
||||
# lire les croquis du dossier
|
||||
dem_croquis = get_dem_notes(request, nodossier, '0', 'CROQUIS')
|
||||
|
||||
if 'form.close' in request.params:
|
||||
status = request.params["status"]
|
||||
@@ -146,13 +148,6 @@ def dossier_view(request):
|
||||
request.session.flash(u"Le dossier a été cloturé avec succès.", 'success')
|
||||
return HTTPFound(url)
|
||||
|
||||
if 'form.addNote' in request.params:
|
||||
type_note = request.params["type_note"]
|
||||
insert_dem_note(request, nodossier, type_note, logged_in)
|
||||
|
||||
request.session.flash(u"Le dossier a été cloturé avec succès.", 'success')
|
||||
return HTTPFound(url)
|
||||
|
||||
return {
|
||||
'page_title': "Dossier : %s" % (nodossier),
|
||||
'url': url,
|
||||
@@ -172,6 +167,7 @@ def dossier_view(request):
|
||||
'motifs': motifs,
|
||||
'motif': '',
|
||||
'dem_notes': dem_notes,
|
||||
'dem_croquis': dem_croquis,
|
||||
}
|
||||
|
||||
@view_config(route_name='dossier_selected', permission='view')
|
||||
@@ -1591,6 +1587,14 @@ def dem_devis(request):
|
||||
'order_option': order_option,
|
||||
}
|
||||
|
||||
@view_config(route_name='note_add', permission='view')
|
||||
def note_add(request):
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
nodossier = request.matchdict['nodossier']
|
||||
insert_dem_note(request, nodossier, 'NOTE', logged_in)
|
||||
|
||||
return HTTPFound(request.route_url('dossier_view', nodossier=nodossier))
|
||||
|
||||
@view_config(route_name='note_edit', renderer='../templates/dossier/note_edit.pt', permission='view')
|
||||
def note_edit(request):
|
||||
nodossier = request.matchdict['nodossier']
|
||||
@@ -1619,3 +1623,11 @@ def note_edit(request):
|
||||
'nodossier': nodossier,
|
||||
'note': note,
|
||||
}
|
||||
|
||||
@view_config(route_name='croquis_edit', renderer='../templates/dossier/croquis_edit.pt', permission='view')
|
||||
def croquis_edit(request):
|
||||
|
||||
return {
|
||||
'page_title': 'NOUVEAU CROQUIS',
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user