diff --git a/ui (gen4)/data.js b/ui (gen4)/data.js index 92feb069..8ce29d72 100755 --- a/ui (gen4)/data.js +++ b/ui (gen4)/data.js @@ -13,6 +13,7 @@ console.log('>>> data') var formats = require('formats') +var sha1 = require('./ext-lib/sha1') module.DATA_VERSION = '3.0' @@ -103,7 +104,7 @@ module.DataClassPrototype = { fromList: function(list){ var res = new Data() // XXX make a real ribbon gid... - var gid = res.newGid('R') + var gid = res.newGid() res.order = list res.ribbon_order.push(gid) res.ribbons[gid] = list.slice() @@ -240,16 +241,42 @@ module.DataPrototype = { return lst }, - // Generate a unique GID... + // Generate a GID... // - // XXX generate a real gid... - newGid: function(prefix){ - prefix = prefix == null ? 'G' : prefix - var gid = prefix + Date.now() + // If no arguments are given then a unique gid will be generated. + // + newGid: function(str, nohash){ + var p = location.hostname + '-' + // if we are on node.js add process pid + if(typeof(process) != 'undefined'){ + p += process.pid + '-' + } + // decide to use a hashing function... + if(typeof(sha1) != 'undefined'){ + var hash = sha1.hash.bind(sha1) + } else { + var hash = function(g){ return g } + } + + // return string as-is... + if(nohash){ + return str || p+Date.now() + } + + // make a hash... + var gid = hash(str || (p+Date.now())) + + // for explicit string return the hash as-is... + if(str != null){ + return gid + } + + // check that the gid is unique... while(this.ribbon_order.indexOf(gid) >= 0 || this.order.indexOf(gid) >= 0){ - gid = prefix + Date.now() + gid = hash(p+Date.now()) } + return gid }, @@ -933,7 +960,7 @@ module.DataPrototype = { // If mode is 'below' this will create a new ribbon below the target, // otherwise the new ribbon will be created above. newRibbon: function(target, mode){ - var gid = this.newGid('R') + var gid = this.newGid() var i = this.getRibbonOrder(target) i = mode == 'below' ? i+1 : i @@ -1369,7 +1396,7 @@ module.DataPrototype = { if(d < 0){ // see if g is unique... if(g in base.ribbons || base.order.indexOf(g) >= 0){ - g = base.newGid('R') + g = base.newGid() } base.ribbon_order.splice(t, 0, g) base.ribbons[g] = r @@ -1385,7 +1412,7 @@ module.DataPrototype = { } else { // see if g is unique... if(g in base.ribbons || base.order.indexOf(g) >= 0){ - g = base.newGid('R') + g = base.newGid() } base.ribbon_order.push(g) base.ribbons[g] = r diff --git a/ui (gen4)/ext-lib/sha1.js b/ui (gen4)/ext-lib/sha1.js new file mode 100755 index 00000000..cdc206b5 --- /dev/null +++ b/ui (gen4)/ext-lib/sha1.js @@ -0,0 +1,158 @@ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* SHA-1 implementation in JavaScript (c) Chris Veness 2002-2014 / MIT Licence */ +/* */ +/* - see http://csrc.nist.gov/groups/ST/toolkit/secure_hashing.html */ +/* http://csrc.nist.gov/groups/ST/toolkit/examples.html */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* jshint node:true *//* global define, escape, unescape */ +'use strict'; + + +/** + * SHA-1 hash function reference implementation. + * + * @namespace + */ +var Sha1 = {}; + + +/** + * Generates SHA-1 hash of string. + * + * @param {string} msg - (Unicode) string to be hashed. + * @returns {string} Hash of msg as hex character string. + */ +Sha1.hash = function(msg) { + // convert string to UTF-8, as SHA only deals with byte-streams + msg = msg.utf8Encode(); + + // constants [§4.2.1] + var K = [ 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 ]; + + // PREPROCESSING + + msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [§5.1.1] + + // convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1] + var l = msg.length/4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length + var N = Math.ceil(l/16); // number of 16-integer-blocks required to hold 'l' ints + var M = new Array(N); + + for (var i=0; i>> 32, but since JS converts + // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators + M[N-1][14] = ((msg.length-1)*8) / Math.pow(2, 32); M[N-1][14] = Math.floor(M[N-1][14]); + M[N-1][15] = ((msg.length-1)*8) & 0xffffffff; + + // set initial hash value [§5.3.1] + var H0 = 0x67452301; + var H1 = 0xefcdab89; + var H2 = 0x98badcfe; + var H3 = 0x10325476; + var H4 = 0xc3d2e1f0; + + // HASH COMPUTATION [§6.1.2] + + var W = new Array(80); var a, b, c, d, e; + for (var i=0; i>>(32-n)); +}; + + +/** + * Hexadecimal representation of a number. + * @private + */ +Sha1.toHexStr = function(n) { + // note can't use toString(16) as it is implementation-dependant, + // and in IE returns signed numbers when used on full words + var s="", v; + for (var i=7; i>=0; i--) { v = (n>>>(i*4)) & 0xf; s += v.toString(16); } + return s; +}; + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +/** Extend String object with method to encode multi-byte string to utf8 + * - monsur.hossa.in/2012/07/20/utf-8-in-javascript.html */ +if (typeof String.prototype.utf8Encode == 'undefined') { + String.prototype.utf8Encode = function() { + return unescape( encodeURIComponent( this ) ); + }; +} + +/** Extend String object with method to decode utf8 string to multi-byte */ +if (typeof String.prototype.utf8Decode == 'undefined') { + String.prototype.utf8Decode = function() { + try { + return decodeURIComponent( escape( this ) ); + } catch (e) { + return this; // invalid UTF-8? return as-is + } + }; +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +if (typeof module != 'undefined' && module.exports) module.exports = Sha1; // CommonJs export +if (typeof define == 'function' && define.amd) define([], function() { return Sha1; }); // AMD diff --git a/ui (gen4)/images.js b/ui (gen4)/images.js index ecf6fbc4..921d1278 100755 --- a/ui (gen4)/images.js +++ b/ui (gen4)/images.js @@ -238,9 +238,13 @@ module.ImagesPrototype = { // function format: // function(key, value, index, object) // + // reduce function format: + // function(value1, value2, key, index, object) + // + // // this will be set to the value... // - // + // XXX revise... // XXX are these slower than doing it manualy via Object.keys(..) forEach: function(func){ var i = 0 @@ -267,6 +271,13 @@ module.ImagesPrototype = { } return res }, + reduce: function(func, initial){ + var res = initial + for(var key in this){ + res = func.call(this[key], res, this[key], key, i++, this) + } + return res + }, keys: function(){ return Object.keys(this) @@ -440,7 +451,8 @@ module.ImagesPrototype = { // .flipImage(target, 'vertical') // -> images // - flipImage: function(gids, direction){ + // XXX add reference support... + flipImage: function(gids, direction, reference){ gids = gids.constructor !== Array ? [gids] : gids var that = this gids.forEach(function(key){ diff --git a/ui (gen4)/viewer.js b/ui (gen4)/viewer.js index b561f0b8..76a72e84 100755 --- a/ui (gen4)/viewer.js +++ b/ui (gen4)/viewer.js @@ -12,6 +12,7 @@ console.log('>>> viewer') var actions = require('lib/actions') var data = require('data') +var images = require('images') var ribbons = require('ribbons') @@ -183,17 +184,15 @@ actions.Actions({ // basic life-cycle actions... // - ready: [ - function(){ - // XXX setup empty state... - }], load: [ function(d){ this.data = data.Data(d.data) + this.images = images.Images(d.images) }], clear: [ function(){ delete this.data + delete this.Images }], @@ -275,7 +274,6 @@ actions.Actions({ nextImageInOrder: ['Focus next image in order', function(){ this.nextImage(this.data.order) }], - firstRibbon: ['Focus previous ribbon', function(){ this.focusRibbon('first') }], lastRibbon: ['Focus next ribbon', @@ -396,6 +394,7 @@ actions.Actions({ // // XXX these are not data stuff... should this be split into a // separate images block??? + // XXX should we have .rotate(..) and .flip(..) generic actions??? rotateCW: [ function(){ }], rotateCCW: [ @@ -511,10 +510,6 @@ actions.Actions(Client, { }, - ready: [ - function(){ - // XXX setup empty state... - }], load: [ function(data){ // recycle the viewer if one is not given specifically... @@ -845,15 +840,11 @@ actions.Actions(Client, { // basic image editing... // - // XXX should these call .images.* or should it be done by data... - // ...I think that data is a better candidate as it should be - // standalone... // XXX should we have .rotate(..) and .flip(..) generic actions??? rotateCW: [ function(target){ this.ribbons.rotateCW(target) }], rotateCCW: [ function(target){ this.ribbons.rotateCCW(target) }], - // XXX tell data/images about the flip... flipVertical: [ function(target){ this.ribbons.flipVertical(target, 'view') }], flipHorizontal: [