diff options
Diffstat (limited to 'public/assets/javascripts')
32 files changed, 1050 insertions, 600 deletions
diff --git a/public/assets/javascripts/app.js b/public/assets/javascripts/app.js index ad3c601..1dd8a5e 100644 --- a/public/assets/javascripts/app.js +++ b/public/assets/javascripts/app.js @@ -98,11 +98,4 @@ app.position = function(obj){ return pos } -app.defaults = { - viewHeight: window.viewHeight = 186, - units: app.units = "ft", - footResolution: 36, - meterResolution: 100, -} - document.addEventListener('DOMContentLoaded', app.init) diff --git a/public/assets/javascripts/defaults.js b/public/assets/javascripts/defaults.js new file mode 100644 index 0000000..5573073 --- /dev/null +++ b/public/assets/javascripts/defaults.js @@ -0,0 +1,12 @@ +app.defaults = { + viewHeight: window.viewHeight = 186, + units: app.units = "ft", + footResolution: 36, + meterResolution: 100, + wallOpacity: 0.95, + wallColor: [255,255,255], + outlineWidth: 2, + outlineColor: [0,0,0], + floorColor: [246,246,246], + ceilingColor: [255,255,255], +} diff --git a/public/assets/javascripts/mx/extensions/mx.movements.js b/public/assets/javascripts/mx/extensions/mx.movements.js index 3b7d3e2..669a7f4 100644 --- a/public/assets/javascripts/mx/extensions/mx.movements.js +++ b/public/assets/javascripts/mx/extensions/mx.movements.js @@ -34,8 +34,10 @@ MX.Movements = function (cam) { init: function () { document.addEventListener('keydown', function (e) { - // console.log(e.keyCode) - if (locked) return; + // console.log(e.keyCode) + if (locked || e.altKey || e.metaKey || e.ctrlKey) { + return + } switch ( e.keyCode ) { case 16: // shift diff --git a/public/assets/javascripts/rectangles/engine/rooms/_rooms.js b/public/assets/javascripts/rectangles/engine/rooms/_rooms.js index 29dee41..5ed7be8 100644 --- a/public/assets/javascripts/rectangles/engine/rooms/_rooms.js +++ b/public/assets/javascripts/rectangles/engine/rooms/_rooms.js @@ -35,7 +35,6 @@ var base = this base.list = {} - base.walls = {} base.regions = [] base.uid = new UidGenerator(base.list) @@ -110,14 +109,6 @@ Rooms.clipper.update() } - base.serializeWalls = function(){ - return [] - } - - base.deserializeWalls = function(walls_data){ - return [] - } - base.sorted_by_position = function(){ return sort.rooms_by_position( base.values() ) } diff --git a/public/assets/javascripts/rectangles/engine/rooms/_walls.js b/public/assets/javascripts/rectangles/engine/rooms/_walls.js new file mode 100644 index 0000000..f2f395b --- /dev/null +++ b/public/assets/javascripts/rectangles/engine/rooms/_walls.js @@ -0,0 +1,134 @@ +(function(){ + + var vec2, Rect, Room, sort, UidGenerator, _ + if ('window' in this) { + vec2 = window.vec2 + Rect = window.Rect + Room = window.Room + sort = window.sort + UidGenerator = window.UidGenerator + _ = window._ + } + else { + vec2 = require('../../models/vec2') + Rect = require('../../models/rect') + Room = require('../../models/room') + UidGenerator = require('../../util/uid') + sort = require('../../util/sort') + _ = require('lodash') + FRONT = 0x1, BACK = 0x2, LEFT = 0x4, RIGHT = 0x8, FLOOR = 0x10, CEILING = 0x20 + TOP = CEILING, BOTTOM = FLOOR + function sidesToString(sides){ + var s = "" + if (sides & FRONT) s += "front " + if (sides & BACK) s += "back " + if (sides & LEFT) s += "left " + if (sides & RIGHT) s += "right " + if (sides & TOP) s += "top " + if (sides & BOTTOM) s += "bottom " + return s + } + } + + var Walls = new function(){ + + var base = this + + base.list = [] + base.lookup = {} + base.colors = {} + + base.first = function(){ + for (var i in base.list) { + if (base.list.hasOwnProperty(i)) { + return base.list[i] + } + } + } + + base.assign = function(list){ + base.list = list + base.lookup = {} + list.forEach(function(wall){ + base.lookup[wall.id] = wall + }) + } + + base.bind = function(){ + base.list.forEach(function(wall){ + wall.bind() + }) + } + + base.count = function(){ + return this.list.length + } + + base.forEach = function(f){ + return base.list.forEach(f) + } + + base.map = function(f){ + return base.list.map(f) + } + + base.serialize = function(){ + var data = [] + base.list.forEach(function(wall){ + data.push(wall.serialize()) + }) + return data + } + + base.deserialize = function(walls_data){ + walls_data.forEach(function(wall_data){ + if (! wall_data) { return } + var wall = base.lookup[ wall_data.id ] + wall.deserialize( wall_data ) + }) + }, + + base.setColor = { + + wall: function(rgb){ + var rgbaColor = rgba_string(rgb, app.defaults.wallOpacity) + Walls.colors.wall = rgb + Walls.forEach(function(wall){ + wall.outline(rgbaColor, null) + }) + }, + + outline: function(rgb){ + var rgbColor = rgb_string(rgb) + Walls.colors.outline = rgb + Walls.forEach(function(wall){ + wall.outline(null, rgbColor) + }) + }, + + floor: function(rgb){ + var rgbColor = rgb_string(rgb) + Walls.colors.floor = rgb + Rooms.forEach(function(room){ + room.setFloorColor(rgbColor) + }) + }, + + ceiling: function(rgb){ + var rgbColor = rgb_string(rgb) + Walls.colors.ceiling = rgb + Rooms.forEach(function(room){ + room.setCeilingColor(rgbColor) + }) + }, + + } + } + + if ('window' in this) { + window.Walls = Walls + } + else { + module.exports = Walls + } +})() diff --git a/public/assets/javascripts/rectangles/engine/rooms/builder.js b/public/assets/javascripts/rectangles/engine/rooms/builder.js index f321f71..f0935d4 100644 --- a/public/assets/javascripts/rectangles/engine/rooms/builder.js +++ b/public/assets/javascripts/rectangles/engine/rooms/builder.js @@ -165,7 +165,6 @@ room.mx_ceiling.push(el) } }.bind(this)) - } else { // render floor and ceiling for the entire rectangle @@ -282,8 +281,8 @@ el.side = CEILING return el } - this.make_wall = function (klass) { + // klass += ".backface-hidden" var el = new MX.Object3D(".face" + (klass || "")) el.width = el.height = el.scaleX = el.scaleY = el.scaleZ = 1 el.z = el.y = el.x = 0 @@ -293,7 +292,7 @@ el.side = 0 el.rect = null el.destroy = function(){ - this.el = this.rect = this.face = null + this.el = this.rect = this.face = null } // possible if walls are opaque diff --git a/public/assets/javascripts/rectangles/engine/rooms/grouper.js b/public/assets/javascripts/rectangles/engine/rooms/grouper.js index cde9fbb..ba510e1 100644 --- a/public/assets/javascripts/rectangles/engine/rooms/grouper.js +++ b/public/assets/javascripts/rectangles/engine/rooms/grouper.js @@ -52,8 +52,8 @@ base.group(walls, collections, BACK) base.group(walls, collections, LEFT) base.group(walls, collections, RIGHT) - Rooms.walls = walls - base.bind() + Walls.assign( walls ) + Walls.bind() } base.collect = function(){ var collections = {} @@ -83,7 +83,10 @@ collection.sort( useX ? sort.compare_zx : sort.compare_xz ) collection.forEach(function(mx){ - if (last_mx && last_mx.rect.eq(mx.rect)) { + if (mx.culled) { + return + } + if (last_mx && mx && last_mx.rect.eq(mx.rect)) { // culls half-walls if (last_mx.rect.id == mx.rect.id) { last_mx.height += mx.height/2 @@ -91,9 +94,11 @@ last_mx.face.y.b += mx.height/2 } last_mx.side = side - mx.culled = true - mx.destroy() - scene.remove(mx) + if (! mx.culled) { + scene.remove(mx) + mx.destroy() + mx.culled = true + } return } widthVec = mx.rect[useX ? 'x' : 'y'].clone() @@ -142,7 +147,6 @@ } } wall = new Wall ({ - id: base.uid(), side: side, mx: [ mx ], surface: new Surface( mx.face ), @@ -155,13 +159,6 @@ return walls } - - base.bind = function(){ - Rooms.walls.forEach(function(wall){ - wall.bind() - wall.randomize_colors() - }) - } } diff --git a/public/assets/javascripts/rectangles/engine/scenery/_scenery.js b/public/assets/javascripts/rectangles/engine/scenery/_scenery.js index 2fd6122..4cf5b06 100644 --- a/public/assets/javascripts/rectangles/engine/scenery/_scenery.js +++ b/public/assets/javascripts/rectangles/engine/scenery/_scenery.js @@ -84,7 +84,7 @@ var Scenery = new function(){ base.deserialize = function(scenery_data){ scenery_data.forEach(function(data){ - var wall = Rooms.walls[data.wall_id] || Rooms.walls[0] + var wall = Walls.list[data.wall_id] || Walls.first() var scene_media = base.add({ data: data, wall: wall, diff --git a/public/assets/javascripts/rectangles/engine/scenery/resize.js b/public/assets/javascripts/rectangles/engine/scenery/resize.js index d9cce18..e26c0a7 100644 --- a/public/assets/javascripts/rectangles/engine/scenery/resize.js +++ b/public/assets/javascripts/rectangles/engine/scenery/resize.js @@ -109,6 +109,7 @@ Scenery.resize = new function(){ } base.hide = function () { + if (! obj) return obj = null dots.forEach(function(dot){ scene.remove(dot) @@ -175,7 +176,8 @@ Scenery.resize = new function(){ mag = y_sign * mag * sign(height) } - obj.mx.scale = ( dimension.a + mag ) / naturalDimension.a // dimension.a // scale * (old_width + mag) / old_width + obj.set_scale( ( dimension.a + mag ) / naturalDimension.a ) + // dimension.a // scale * (old_width + mag) / old_width // console.log(scale, obj.mx.scale, dimension.a + mag, naturalDimension.a) @@ -188,6 +190,8 @@ Scenery.resize = new function(){ } base.move_dots() + + app.router.editorView.mediaEditor.setDimensions() } function up (e, cursor){ diff --git a/public/assets/javascripts/rectangles/engine/scenery/undo.js b/public/assets/javascripts/rectangles/engine/scenery/undo.js index 54ab755..e5624a0 100644 --- a/public/assets/javascripts/rectangles/engine/scenery/undo.js +++ b/public/assets/javascripts/rectangles/engine/scenery/undo.js @@ -1,7 +1,7 @@ (function(){ - UndoStack.register([ - { - type: "create-scenery", + UndoStack.register([ + { + type: "create-scenery", undo: function(state){ Scenery.remove(state.id) @@ -14,13 +14,13 @@ // TODO: watch individual scenery object here Minotaur.watch( app.router.editorView.settings ) }, - }, - { - type: "update-scenery", + }, + { + type: "update-scenery", undo: function(state){ var scenery = Scenery.find(state.id) scenery.deserialize(state) - scenery.set_wall(Rooms.walls[ state.wall_id ]) + scenery.set_wall(Walls.find( state.wall_id )) if (editor.permissions.resize) { Scenery.resize.show(scenery) @@ -32,7 +32,7 @@ redo: function(state){ var scenery = Scenery.find(state.id) scenery.deserialize(state) - scenery.set_wall(Rooms.walls[ state.wall_id ]) + scenery.set_wall(Walls.find( state.wall_id )) if (editor.permissions.resize) { Scenery.resize.show(scenery) @@ -43,9 +43,9 @@ // TODO: watch individual scenery object here Minotaur.watch( app.router.editorView.settings ) }, - }, - { - type: "destroy-scenery", + }, + { + type: "destroy-scenery", undo: function(state){ Scenery.deserialize([ state ]) @@ -58,12 +58,12 @@ // TODO: watch individual scenery object here Minotaur.watch( app.router.editorView.settings ) }, - }, - - // - - { - type: "create-room", + }, + + // + + { + type: "create-room", undo: function(room){ Rooms.remove(room) Rooms.clipper.update() @@ -71,48 +71,59 @@ redo: function(room){ Rooms.add(new Room(room)) Rooms.clipper.update() - app.tube("builder-pick-room", room) + app.tube("builder-pick-room", room) }, - }, - { - type: "update-room", + }, + { + type: "update-room", undo: function(state){ var room = Rooms.list[state.id] room.rect.assign( state.rect ) room.height = state.height Rooms.clipper.update() - app.tube("builder-pick-room", room) + app.tube("builder-pick-room", room) }, redo: function(state){ var room = Rooms.list[state.id] room.rect.assign( state.rect ) room.height = state.height Rooms.clipper.update() - app.tube("builder-pick-room", room) + app.tube("builder-pick-room", room) }, - }, - { - type: "destroy-room", + }, + { + type: "destroy-room", undo: function(room){ Rooms.add(new Room(room)) Rooms.clipper.update() - app.tube("builder-pick-room", room) + app.tube("builder-pick-room", room) }, redo: function(room){ Rooms.remove(room) Rooms.clipper.update() }, - }, - - // + }, + + // - { - type: "update-wallpaper", + { + type: "update-wallpaper", undo: function(state){ + var wall = Walls.lookup[state.id] + wall.deserialize(state) + + Minotaur.watch( app.router.editorView.settings ) }, - redo: function(state){ + }, + { + type: "update-colors", + undo: function(state){ + Walls.setColor[ state.mode ]( state.rgb ) + app.router.editorView.lightControl.setSwatchColor( state.mode, state.rgb ) + + Minotaur.watch( app.router.editorView.settings ) }, - }, - - ]) + }, + + ]) })() diff --git a/public/assets/javascripts/rectangles/models/room.js b/public/assets/javascripts/rectangles/models/room.js index 33a94d0..0f09325 100644 --- a/public/assets/javascripts/rectangles/models/room.js +++ b/public/assets/javascripts/rectangles/models/room.js @@ -32,9 +32,6 @@ this.id = opt.id || Rooms.uid("room_") this.rect = opt.rect this.regions = [] - this.walls = [] - this.floor = [] - this.ceiling = [] this.height = opt.height || 200 this.focused = false @@ -69,30 +66,10 @@ this.intersects = [] this.constructed = false - this.walls = [] - this.mx_walls = [] this.mx_floor = [] this.mx_ceiling = [] } - - Room.prototype.bind = function(){ - var base = this - base.mx_walls.forEach(function(wall){ - $(wall.el).bind({ - mouseover: function(){ - }, - mousemove: function(e){ - var color = choice(window.palettes.colors) - base.mx_walls.forEach(function(wall){ - $(wall.el).css("background-color", color) - }) - }, - mousedown: function(){ - } - }) - }) - } Room.prototype.clipTo = function(r){ // for each of this rect's regions split the region if necessary @@ -182,6 +159,18 @@ return collision } + Room.prototype.setFloorColor = function(rgbColor) { + this.mx_floor.map(function(mx){ + mx.el.style.backgroundColor = rgbColor + }) + } + + Room.prototype.setCeilingColor = function(rgbColor) { + this.mx_ceiling.map(function(mx){ + mx.el.style.backgroundColor = rgbColor + }) + } + if ('window' in this) { window.Room = Room } diff --git a/public/assets/javascripts/rectangles/models/wall.js b/public/assets/javascripts/rectangles/models/wall.js index 8723c3c..6043181 100644 --- a/public/assets/javascripts/rectangles/models/wall.js +++ b/public/assets/javascripts/rectangles/models/wall.js @@ -13,12 +13,13 @@ } var Wall = function(opt){ - this.id = opt.id + this.id = [ opt.side, opt.edge, opt.vec.a ].join("_") this.vec = opt.vec this.edge = opt.edge this.side = opt.side this.surface = opt.surface this.mx = opt.mx + this.background = "" } Wall.prototype.toString = function(){ @@ -63,26 +64,49 @@ e.stopPropagation() return } - + UndoStack.push({ type: 'create-scenery', undo: { id: scenery.id }, redo: scenery.serialize(), }) - - // TODO: watch individual scenery object here - Minotaur.watch( app.router.editorView.settings ) } else if (Scenery.nextWallpaper) { - base.wallpaper() - } + var oldState = base.serialize() + base.wallpaper(Scenery.nextWallpaper) + Scenery.nextWallpaper = null + + UndoStack.push({ + type: 'update-wallpaper', + undo: oldState, + redo: base.serialize(), + }) + } else { app.controller.hideExtras() } } }) }) - this.outline() + + // flip the mx order + var shouldFlip = this.side & (LEFT | BACK) + if (! shouldFlip) { + this.mx.reverse() + } + + // this.outline(wallColor, outlineColor) + } + + Wall.prototype.serialize = function(){ + return { + id: this.id, + background: this.background, + } + } + + Wall.prototype.deserialize = function(data){ + this.wallpaper( data.background ) } @@ -140,143 +164,63 @@ position.a -= dimension.a / 2 position.b -= dimension.b / 2 } -// if (mx.width) { position.a -= mx.width / 2 } -// if (mx.height) { position.b -= mx.height / 2 } return position } - Wall.prototype.bounds_for = function(img, scale) { - scale = scale || 1 - var coord = this.side & FRONT_BACK ? this.rect.x : this.rect.y - var halfWidth = img.width/2 * scale - var halfHeight = img.height/2 * scale - - return new Rect( new vec2( coord.a + halfWidth, coord.b - halfWidth ), - new vec2( halfHeight, Rooms.list[this.room].height - halfHeight ) ) - } - - Wall.prototype.fits = function(img, scale){ - if (this.side & FRONT_BACK && this.rect.x.length() < img.width * scale) { - return false - } - if (this.side & LEFT_RIGHT && this.rect.y.length() < img.width * scale) { - return false - } - return true - } - - Wall.prototype.center = function(offset){ - - switch (this.side) { - case FRONT: - x = this.vec.midpoint() - z = this.edge + painting_distance_from_wall - break - case BACK: - x = this.vec.midpoint() - z = this.edge - painting_distance_from_wall - break - case LEFT: - x = this.edge + painting_distance_from_wall - z = this.vec.midpoint() - break - case RIGHT: - x = this.edge - painting_distance_from_wall - z = this.vec.midpoint() - break - } - - return new vec2 (x, z) - } - Wall.prototype.color = function(color){ - this.$walls && this.$walls.css("background-color", color) + this.$walls.css("background-color", color) } - Wall.prototype.wallpaper = function(){ + Wall.prototype.wallpaper = function(background){ var useX = this.side & FRONT_BACK var shouldFlip = this.side & (LEFT | BACK) + + this.background = background || "none" + this.mx.forEach(function(mx){ var partitionOffset = useX ? mx.x : mx.z if (shouldFlip) partitionOffset *= -1 partitionOffset += mx.width/2 var floorOffset = mx.y + mx.height/2 - mx.el.style.backgroundImage = Scenery.nextWallpaper + mx.el.style.backgroundImage = background mx.el.style.backgroundPosition = (~~partitionOffset) + "px " + (~~floorOffset) + "px" }) } - Wall.prototype.outline = function(){ + Wall.prototype.outline = function(wallColor, outlineColor){ var useX = this.side & FRONT_BACK - var shouldFlip = this.side & (LEFT | BACK) var mx = this.mx - if (! shouldFlip) { - mx = mx.reverse() - } - var len = this.mx.length - - var backgroundColor = "rgba(255,255,255,0.95)" - var borderColor = "rgba(0,0,0,1.0)" - - zz = window.zz || 0 + var outlineString = app.defaults.outlineWidth + "px solid " + outlineColor + zz = 0 mx.forEach(function(mx, i){ - if (mx.outlined) return - mx.outlined = true - mx.el.style.backgroundColor = backgroundColor - - // all walls get bottom lines - mx.el.style.borderBottom = "1px solid " + borderColor - - // all walls get top lines - mx.el.style.borderTop = "1px solid " + borderColor - - // walls on initial sides get left lines - // if their left edge lines up with the rect edge - if (i == 0) { - mx.el.style.borderLeft = "1px solid " + borderColor - } - - // walls on terminal sides get right lines.... - // if their right edge lines up with the rect edge - if (i == len-1) { - mx.el.style.borderRight = "1px solid " + borderColor - } - }) - } - - Wall.prototype.siblings = function(){ - return this -// var base = this -// var match = base.side | base.half_side -// var walls = Rooms.list[this.room].walls.filter(function(w){ -// return (w.side | w.half_side) & match && w.$walls -// }) -// return walls - } - - Wall.prototype.randomize_colors = function(){ - var color = window.grayColors[ this.side | this.half_side ] - // this.color(color) - } - - Wall.prototype.stroke_colors = function(){ - var color = "#fff" - var siblings = this.siblings() - siblings.forEach(function(w, i){ - if (! w.$walls) return - w.color(color) - if (i == 0) { - w.$walls.css("border-left", "1px solid #000") - } - if (i == siblings.length-1) { - w.$walls.css("border-right", "1px solid #000") - } - w.$walls.css("border-top", "1px solid #000") - w.$walls.css("border-bottom", "1px solid #000") + // if (mx.outlined) return + // mx.outlined = true + if (wallColor) { + mx.el.style.backgroundColor = wallColor + } + if (outlineColor) { + // all walls get bottom lines + mx.el.style.borderBottom = outlineString + + // all walls get top lines + mx.el.style.borderTop = outlineString + + // walls on initial sides get left lines + // if their left edge lines up with the rect edge + if (i == 0) { + mx.el.style.borderLeft = outlineString + } + + // walls on terminal sides get right lines.... + // if their right edge lines up with the rect edge + if (i == len-1) { + mx.el.style.borderRight = outlineString + } + } }) } diff --git a/public/assets/javascripts/rectangles/util/colors.js b/public/assets/javascripts/rectangles/util/colors.js index 16d34dd..4ad96fc 100644 --- a/public/assets/javascripts/rectangles/util/colors.js +++ b/public/assets/javascripts/rectangles/util/colors.js @@ -50,11 +50,6 @@ var select = document.querySelector("#palette") select && select.addEventListener("change", function(){ colors = color_palettes[select.value] - Rooms.forEach(function(room){ - room.walls.forEach(function(wall){ - wall.randomize_colors() - }) - }) select.blur() }) diff --git a/public/assets/javascripts/rectangles/util/minotaur.js b/public/assets/javascripts/rectangles/util/minotaur.js index 039a053..e6a37e0 100644 --- a/public/assets/javascripts/rectangles/util/minotaur.js +++ b/public/assets/javascripts/rectangles/util/minotaur.js @@ -4,7 +4,7 @@ var base = this base.$el = $("#minotaur") base.timeout = null - base.delay = 500 + base.delay = 1000 base.objects = {} base.init = function () { diff --git a/public/assets/javascripts/rectangles/util/mouse.js b/public/assets/javascripts/rectangles/util/mouse.js index 34d3f5e..cb36038 100644 --- a/public/assets/javascripts/rectangles/util/mouse.js +++ b/public/assets/javascripts/rectangles/util/mouse.js @@ -1,65 +1,66 @@ /* - usage: - - base.mouse = new mouse({ - el: document.querySelector("#map"), - down: function(e, cursor){ - // do something with val - // cursor.x.a - // cursor.y.a - }, - move: function(e, cursor){ - // delta.a (x) - // delta.b (y) - }, - up: function(e, cursor, new_cursor){ - // cursor.x.a - // cursor.y.a - }, - }) + usage: + + base.mouse = new mouse({ + el: document.querySelector("#map"), + down: function(e, cursor){ + // do something with val + // cursor.x.a + // cursor.y.a + }, + move: function(e, cursor){ + // var delta = cursor.delta() + // delta.a (x) + // delta.b (y) + }, + up: function(e, cursor, new_cursor){ + // cursor.x.a + // cursor.y.a + }, + }) */ function mouse (opt) { - var base = this + var base = this - opt = defaults(opt, { - el: null, - down: null, - move: null, - drag: null, - enter: null, - up: null, - rightclick: null, - propagate: false, - locked: false, - use_offset: true, - val: 0, - }) - - base.down = false + opt = defaults(opt, { + el: null, + down: null, + move: null, + drag: null, + enter: null, + up: null, + rightclick: null, + propagate: false, + locked: false, + use_offset: true, + val: 0, + }) + + base.down = false - base.creating = false - base.dragging = false + base.creating = false + base.dragging = false - base.cursor = new Rect(0,0,0,0) + base.cursor = new Rect(0,0,0,0) - base.tube = new Tube () - opt.down && base.tube.on("down", opt.down) - opt.move && base.tube.on("move", opt.move) - opt.drag && base.tube.on("drag", opt.drag) - opt.enter && base.tube.on("enter", opt.enter) - opt.leave && base.tube.on("leave", opt.leave) - opt.up && base.tube.on("up", opt.up) - opt.rightclick && base.tube.on("rightclick", opt.rightclick) - - var offset = (opt.use_offset && opt.el) ? opt.el.getBoundingClientRect() : null - - base.init = function (){ - base.bind() - } + base.tube = new Tube () + opt.down && base.tube.on("down", opt.down) + opt.move && base.tube.on("move", opt.move) + opt.drag && base.tube.on("drag", opt.drag) + opt.enter && base.tube.on("enter", opt.enter) + opt.leave && base.tube.on("leave", opt.leave) + opt.up && base.tube.on("up", opt.up) + opt.rightclick && base.tube.on("rightclick", opt.rightclick) + + var offset = (opt.use_offset && opt.el) ? opt.el.getBoundingClientRect() : null + + base.init = function (){ + base.bind() + } - base.on = function(){ + base.on = function(){ base.tube.on.apply(base.tube, arguments) } @@ -67,104 +68,104 @@ function mouse (opt) { base.tube.off.apply(base.tube, arguments) } - base.bind = function(){ - if (opt.el) { - opt.el.addEventListener("mousedown", base.mousedown) - opt.el.addEventListener("contextmenu", base.contextmenu) - } - window.addEventListener("mousemove", base.mousemove) - window.addEventListener("mouseup", base.mouseup) - } + base.bind = function(){ + if (opt.el) { + opt.el.addEventListener("mousedown", base.mousedown) + opt.el.addEventListener("contextmenu", base.contextmenu) + } + window.addEventListener("mousemove", base.mousemove) + window.addEventListener("mouseup", base.mouseup) + } - base.bind_el = function(el){ - el.addEventListener("mousedown", base.mousedown) - el.addEventListener("mousemove", base.mousemove) - } - base.unbind_el = function(el){ - el.removeEventListener("mousedown", base.mousedown) - el.removeEventListener("mousemove", base.mousemove) - } + base.bind_el = function(el){ + el.addEventListener("mousedown", base.mousedown) + el.addEventListener("mousemove", base.mousemove) + } + base.unbind_el = function(el){ + el.removeEventListener("mousedown", base.mousedown) + el.removeEventListener("mousemove", base.mousemove) + } - function positionFromMouse(e) { - if (offset) { - return new vec2(offset.left - e.pageX, e.pageY - offset.top) - } - else { - return new vec2(e.pageX, e.pageY) - } - } - - base.mousedown = function(e){ - if (opt.use_offset) { - offset = this.getBoundingClientRect() - } - - var pos = positionFromMouse(e) - - var x = pos.a, y = pos.b - base.cursor = new Rect (x,y, x,y) - base.down = true - e.clickAccepted = true - - base.tube("down", e, base.cursor) + function positionFromMouse(e) { + if (offset) { + return new vec2(offset.left - e.pageX, e.pageY - offset.top) + } + else { + return new vec2(e.pageX, e.pageY) + } + } + + base.mousedown = function(e){ + if (opt.use_offset) { + offset = this.getBoundingClientRect() + } + + var pos = positionFromMouse(e) + + var x = pos.a, y = pos.b + base.cursor = new Rect (x,y, x,y) + base.down = true + e.clickAccepted = true + + base.tube("down", e, base.cursor) - if (e.clickAccepted) { - e.stopPropagation() - } - else { - base.down = false - } - } - base.mousemove = function(e){ - if (opt.use_offset && ! offset) return - - var pos = positionFromMouse(e) + if (e.clickAccepted) { + e.stopPropagation() + } + else { + base.down = false + } + } + base.mousemove = function(e){ + if (opt.use_offset && ! offset) return + + var pos = positionFromMouse(e) - if (e.shiftKey) { - pos.quantize(10) - } + if (e.shiftKey) { + pos.quantize(10) + } - var x = pos.a, y = pos.b - - if (base.down) { - base.cursor.x.b = x - base.cursor.y.b = y - base.tube("drag", e, base.cursor) - e.stopPropagation() - } - else { - base.cursor.x.a = base.cursor.x.b = x - base.cursor.y.a = base.cursor.y.b = y - base.tube("move", e, base.cursor) - } - } - base.mouseenter = function(e, target, index){ - if (! base.down) return - if (opt.use_offset && ! offset) return - base.tube("enter", e, target, base.cursor) - } - base.mouseleave = function(e, target){ - if (! base.down) return - if (opt.use_offset && ! offset) return - base.tube("leave", e, target, base.cursor) - } - base.mouseup = function(e){ - var pos, new_cursor - - if (base.down) { - e.stopPropagation() - base.down = false - pos = positionFromMouse(e) - new_cursor = new Rect (pos.a, pos.b) - base.tube("up", e, base.cursor, new_cursor) - base.cursor = new_cursor - } - } - base.contextmenu = function(e){ - e.preventDefault() - base.tube("rightclick", e, base.cursor) - } + var x = pos.a, y = pos.b + + if (base.down) { + base.cursor.x.b = x + base.cursor.y.b = y + base.tube("drag", e, base.cursor) + e.stopPropagation() + } + else { + base.cursor.x.a = base.cursor.x.b = x + base.cursor.y.a = base.cursor.y.b = y + base.tube("move", e, base.cursor) + } + } + base.mouseenter = function(e, target, index){ + if (! base.down) return + if (opt.use_offset && ! offset) return + base.tube("enter", e, target, base.cursor) + } + base.mouseleave = function(e, target){ + if (! base.down) return + if (opt.use_offset && ! offset) return + base.tube("leave", e, target, base.cursor) + } + base.mouseup = function(e){ + var pos, new_cursor + + if (base.down) { + e.stopPropagation() + base.down = false + pos = positionFromMouse(e) + new_cursor = new Rect (pos.a, pos.b) + base.tube("up", e, base.cursor, new_cursor) + base.cursor = new_cursor + } + } + base.contextmenu = function(e){ + e.preventDefault() + base.tube("rightclick", e, base.cursor) + } - base.init() + base.init() } diff --git a/public/assets/javascripts/rectangles/util/undostack.js b/public/assets/javascripts/rectangles/util/undostack.js index b93c79e..959e3d1 100644 --- a/public/assets/javascripts/rectangles/util/undostack.js +++ b/public/assets/javascripts/rectangles/util/undostack.js @@ -31,16 +31,17 @@ this.types[ action.type ].redo(action.redo) return this.pointer < this.stack.length-1 } - UndoStack.prototype.register = function(actionType){ - if (actionType.length) { - actionType.forEach(this.registerOne.bind(this)) + UndoStack.prototype.register = function(actions){ + if (actions.length) { + actions.forEach(this.registerOne.bind(this)) } else { - this.registerOne(actionType) + this.registerOne(actions) } } - UndoStack.prototype.registerOne = function(actionType){ - this.types[ actionType.type ] = actionType + UndoStack.prototype.registerOne = function(action){ + if (! action.redo) { action.redo = action.undo } + this.types[ action.type ] = action } if ('window' in this) { window.UndoStack = new UndoStack diff --git a/public/assets/javascripts/ui/builder/BuilderSettings.js b/public/assets/javascripts/ui/builder/BuilderSettings.js index 0091454..796c398 100644 --- a/public/assets/javascripts/ui/builder/BuilderSettings.js +++ b/public/assets/javascripts/ui/builder/BuilderSettings.js @@ -29,6 +29,8 @@ var BuilderSettings = FormView.extend({ this.$id.val(data._id) this.$name.val(data.name) + this.parent.lightControl.loadDefaults() + data.rooms && Rooms.deserialize(data.rooms) data.startPosition && scene.camera.move(data.startPosition) data.privacy && this.$privacy.find("[value=" + data.privacy + "]").prop('checked', "checked") diff --git a/public/assets/javascripts/ui/builder/BuilderView.js b/public/assets/javascripts/ui/builder/BuilderView.js index 555cd58..a89111f 100644 --- a/public/assets/javascripts/ui/builder/BuilderView.js +++ b/public/assets/javascripts/ui/builder/BuilderView.js @@ -11,6 +11,7 @@ var BuilderView = View.extend({ this.info = new BuilderInfo ({ parent: this }) this.toolbar = new BuilderToolbar ({ parent: this }) this.settings = new BuilderSettings ({ parent: this }) + this.lightControl = new LightControl ({ parent: this }) }, load: function(name){ diff --git a/public/assets/javascripts/ui/editor/Collaborators.js b/public/assets/javascripts/ui/editor/Collaborators.js new file mode 100644 index 0000000..452ad15 --- /dev/null +++ b/public/assets/javascripts/ui/editor/Collaborators.js @@ -0,0 +1,128 @@ + +var Collaborators = ModalFormView.extend({ + el: ".mediaDrawer.collaborators", + + template: $("#collaborator-template").html(), + + indexAction: function(){ return "/api/collaborator/" + this.parent.data.slug + "/index" }, + createAction: function(){ return "/api/collaborator/" + this.parent.data.slug + "/create" }, + destroyAction: function(){ return "/api/collaborator/" + this.parent.data.slug + "/destroy" }, + + events: { + "keydown [name=email]": "enterSubmit", + "click [data-role=destroy-collaborator]": "destroy", + }, + + show: function(){ + this.action = this.createAction + this.$("#collaborator-url-rapper").hide() + + if (! this.loaded) { + this.load() + } + else { + this.__super__.show.call(this) + } + }, + + load: function(){ + $.get(this.indexAction(), this.populate.bind(this)) + }, + + populate: function(collaborators){ + this.loaded = true + collaborators.forEach(function(collab){ + var $el = $( this.template ) + $el.data("collaborator-id", collab._id) + + if (collab.user) { + $el.find(".email").remove() + + $el.find(".user") + .attr("href", "/profile/" + collab.user.username) + + $el.find(".avatar") + .css("background-image", "url(" + collab.user.photo + ")") + + $el.find(".username") + .html( collab.user.displayName ) + + if (collab.owner) { + $el.find("button").remove() + } + else { + $el.find(".role").remove() + } + } + else { + $el.find(".user").remove() + $el.find(".role").remove() + $el.find(".email").html( collab.email ) + } + + $("#collaborator-list").append($el) + }.bind(this)) + + this.__super__.show.call(this) + }, + + enterSubmit: function (e) { + e.stopPropagation() + var base = this + if (e.keyCode == 13) { + setTimeout(function(){ + base.save(e) + base.reset() + }, 100) + } + }, + + validate: function(){ + var errors = [] + + var email = this.$("[name=email]").val() + if (! email.length) { + errors.push("Please enter an email address"); + } + else if (email.indexOf("@") === -1) { + errors.push("Please enter a valid email address"); + } + + return errors + }, + + success: function(data){ + this.reset() + this.populate([data]) + // weird! this.$("#collaborator-url") not working here, but works without this + setTimeout(function(){ + $("#collaborator-url").val("http://vvalls.com/join/" + data.nonce) + }, 100) + this.$("#collaborator-dummy-email").html(data.email) + this.$("#collaborator-url-rapper").slideDown(300) + }, + + destroy: function(e){ + var base = this + var $el = $(e.currentTarget).closest("li") + + if ($el.find(".user").length) { + var name = $el.find(".username").html() + ConfirmModal.confirm("Are you sure you want to remove " + name + " from this project?", _destroy) + } + else { + _destroy() + } + + function _destroy () { + var _id = $el.data("collaborator-id") + $el.remove() + $.ajax({ + type: "DELETE", + url: base.destroyAction(), + data: { _id: _id, _csrf: $("[name=_csrf]").val() }, + }) + } + }, + +}) diff --git a/public/assets/javascripts/ui/editor/EditorSettings.js b/public/assets/javascripts/ui/editor/EditorSettings.js index e9239e4..2a3929a 100644 --- a/public/assets/javascripts/ui/editor/EditorSettings.js +++ b/public/assets/javascripts/ui/editor/EditorSettings.js @@ -9,6 +9,7 @@ var EditorSettings = FormView.extend({ events: { "keydown": 'stopPropagation', "keydown [name=name]": 'enterSubmit', + "click [data-role='show-collaborators']": 'showCollaborators', "click [data-role='save-project']": 'save', "click [data-role='clone-project']": 'clone', "click [data-role='clear-project']": 'clear', @@ -28,15 +29,23 @@ var EditorSettings = FormView.extend({ load: function(data){ this.action = data.isNew ? this.createAction : this.updateAction - + this.parent.data = data + data.rooms && Rooms.deserialize(data.rooms) + data.walls && Walls.deserialize(data.walls) data.startPosition && scene.camera.move(data.startPosition) - + + if (data.colors && data.colors.wall) { + this.parent.lightControl.load(data.colors) + } + else { + this.parent.lightControl.loadDefaults() + } + if (data.isNew) { this.$name.val( "Room " + moment().format("DD/MM/YYYY ha") ) } else { - // console.log(data) this.thumbnailIsStale() this.$id.val( data._id ) @@ -48,6 +57,11 @@ var EditorSettings = FormView.extend({ } }, + showCollaborators: function(e){ + e && e.preventDefault() + this.parent.collaborators.show() + }, + clone: function(){ var names = this.$name.val().split(" ") if ( ! isNaN(Number( names[names.length-1] )) ) { @@ -122,7 +136,8 @@ var EditorSettings = FormView.extend({ fd.append( "description", this.$description.val() ) fd.append( "privacy", this.$privacy.filter(":checked").val() == "private" ) fd.append( "rooms", JSON.stringify( Rooms.serialize() ) ) - fd.append( "walls", JSON.stringify( Rooms.serializeWalls() ) ) + fd.append( "walls", JSON.stringify( Walls.serialize() ) ) + fd.append( "colors", JSON.stringify( Walls.colors ) ) fd.append( "media", JSON.stringify( Scenery.serialize() ) ) fd.append( "startPosition", JSON.stringify( app.position(scene.camera) ) ) @@ -152,6 +167,8 @@ var EditorSettings = FormView.extend({ Minotaur.hide() window.history.pushState(null, document.title, "/project/" + data.slug + "/edit") + + this.parent.data = data }, }) diff --git a/public/assets/javascripts/ui/editor/EditorToolbar.js b/public/assets/javascripts/ui/editor/EditorToolbar.js index 5e0da7e..c631317 100644 --- a/public/assets/javascripts/ui/editor/EditorToolbar.js +++ b/public/assets/javascripts/ui/editor/EditorToolbar.js @@ -56,6 +56,11 @@ var EditorToolbar = View.extend({ $(".inuse").removeClass("inuse") $("[data-role='resize-media']").toggleClass("inuse", state) if (state) { + if (this.parent.mediaEditor.scenery) { + Scenery.resize.show( this.parent.mediaEditor.scenery ) + } + } + else { Scenery.resize.hide() } }, diff --git a/public/assets/javascripts/ui/editor/EditorView.js b/public/assets/javascripts/ui/editor/EditorView.js index 4067c4d..e11f189 100644 --- a/public/assets/javascripts/ui/editor/EditorView.js +++ b/public/assets/javascripts/ui/editor/EditorView.js @@ -16,6 +16,7 @@ var EditorView = View.extend({ this.mediaEditor = new MediaEditor ({ parent: this }) this.wallpaperPicker = new WallpaperPicker ({ parent: this }) this.lightControl = new LightControl ({ parent: this }) + this.collaborators = new Collaborators ({ parent: this }) }, load: function(name){ @@ -30,7 +31,7 @@ var EditorView = View.extend({ ready: function(data){ $("#map").hide() - + this.settings.load(data) }, diff --git a/public/assets/javascripts/ui/editor/LightControl.js b/public/assets/javascripts/ui/editor/LightControl.js index c3e80c2..bd09dc2 100644 --- a/public/assets/javascripts/ui/editor/LightControl.js +++ b/public/assets/javascripts/ui/editor/LightControl.js @@ -1,43 +1,272 @@ var LightControl = View.extend({ - el: ".lightcontrol", - - events: { - "mousedown": "stopPropagation", - }, + el: ".lightcontrol", + + events: { + "mousedown": "stopPropagation", + "click .swatch": "clickSwatch", + "click label": "clickLabel", + "input #shadow-control": "updateShadow", + "mousedown #brightness-control": "beginBrightness", + "input #brightness-control": "updateBrightness", + "input #outline-hue": "updateShadow", + "input #wall-hue": "updateShadow", + }, + + initialize: function(){ + this.colorPicker = new LabColorPicker(this, 180, 180) + this.$el.prepend( this.colorPicker.canvas ) + + this.$swatches = this.$(".swatch") + this.$labels = this.$(".swatch + label") + this.$swatch = { + wall: this.$("#wall-color"), + outline: this.$("#outline-color"), + floor: this.$("#floor-color"), + ceiling: this.$("#ceiling-color"), + } + this.$brightnessControl = this.$("#brightness-control") + }, - toggle: function(state){ + modes: [ "wall", "outline", "floor", "ceiling" ], + + load: function(data){ + this.modes.forEach(function(mode){ + Walls.setColor[mode](data[mode]) + this.$swatch[ mode ].css("background-color", rgb_string(data[mode])) + }.bind(this)) + this.setMode("wall") + }, + + loadDefaults: function(){ + var colors = { + wall: app.defaults.wallColor.slice(), + outline: app.defaults.outlineColor.slice(), + floor: app.defaults.floorColor.slice(), + ceiling: app.defaults.ceilingColor.slice(), + } + this.load(colors) + }, + + toggle: function(state){ this.$el.toggleClass("active", state); + }, + + show: function(){ + this.toggle(true) + }, + + hide: function(){ + this.toggle(false) + }, + + pick: function(rgb, Lab){ + this.labColor = Lab + this.setSwatchColor(this.mode, rgb) + Walls.setColor[ this.mode ](rgb) + }, + + setSwatchColor: function(mode, rgb) { + this.$swatch[ mode ].css("background-color", rgb_string(rgb)) + }, + + initialState: null, + + begin: function(){ + this.initialState = this.serialize() + }, + + serialize: function(){ + return { + mode: this.mode, + rgb: Walls.colors[ this.mode ] + } + }, + + finalize: function(){ + if (! this.initialState) { return } + UndoStack.push({ + type: 'update-colors', + undo: this.initialState, + redo: this.serialize(), + }) + this.initialState = null + }, + + setMode: function (mode) { + var color, brightness + this.mode = mode + this.$swatches.removeClass("selected") + this.$labels.removeClass("selected") + this.$swatch[ mode ].addClass("selected") + color = Walls.colors[ mode ] + + this.$(".swatch.selected").next("label").addClass("selected") + this.labColor = this.colorPicker.load(color) + this.$brightnessControl.val( this.labColor[0] ) + }, + + clickLabel: function(e){ + $(e.currentTarget).prev(".swatch").trigger("click") + }, + clickSwatch: function(e){ + var mode = $(e.currentTarget).data('mode') + this.setMode(mode) + }, + + beginBrightness: function(){ + this.begin() + $(window).one("mouseup", this.finalize.bind(this)) + }, + + updateBrightness: function(){ + this.labColor[0] = parseFloat( this.$brightnessControl.val() ) + var rgb = this.colorPicker.setLab( this.labColor ) + this.pick(rgb, this.labColor) + }, - // toggle the class that makes the cursor a paintbucket - // $("body").removeClass("pastePaper"); - }, - show: function(){ - this.toggle(true) - }, - hide: function(){ - this.toggle(false) - }, +}) -/* - $("#shadow-control").on({ - mousedown: function(){ app.dragging = true }, - change: function(){ - var hex = (~~($(this).int() / 100 * 0xff)).toString(10) - if (hex.length == 1) hex = "0" + hex; - var color = "rgba(" + [hex, hex, hex, "1.0"] + ")" - $(".face").css("border-color", color) - } - }) +var LabColorPicker = function (parent, w, h) { + var base = this + var canvas = this.canvas = document.createElement('canvas') + var ctx = this.ctx = canvas.getContext('2d') + var imageData = ctx.createImageData(w,h) + var data = imageData.data - $("#brightness-control").on({ - mousedown: function(){ app.dragging = true }, - change: function(){ - var hex = (~~($(this).int() / 100 * 0xff)).toString(10) - var color = "rgba(" + [hex, hex, hex, "0.9"] + ")" - $("body,.face").css("background-color", color) - } - }) -*/ +// var cursor = this.cursor = document.createElement("div") +// cursor.className = "colorPickerCursor" -}) + canvas.width = w + canvas.height = h + canvas.className = "colorPicker" + + var ww = w-1 + var hh = h-1 + + var L_range = [0, 110] + var a_range = [-86.185, 98.254] + var b_range = [-107.863, 94.482] + + var rgb = [0,0,0] + + var val = 80 + + this.mouse = new mouse({ + el: canvas, + down: function(e, cursor){ + parent.begin() + cursor.x.a = -cursor.x.a + base.pick(cursor.x.a, cursor.y.a) + }, + drag: function(e, cursor){ + cursor.x.b = -cursor.x.b + base.pick(cursor.x.b, cursor.y.b) + }, + up: function(){ + parent.finalize() + } + }) + + this.setLab = function(Lab) { + val = Lab[0] + this.paint() + var rgb = xyz2rgb(hunterlab2xyz(Lab[0], Lab[1], Lab[2])).map(Math.round) + return rgb + } + this.pick = function(i, j){ + var x = mix( i/ww, a_range[0], a_range[1] ) + var y = mix( j/hh, b_range[0], b_range[1] ) + var rgb = xyz2rgb(hunterlab2xyz(val, x, y)).map(Math.round) + parent.pick( rgb, [val,x,y] ) + } + this.load = function(rgba){ + var Lab = xyz2hunterlab(rgb2xyz(rgba)) + var val = clamp( Lab[0], L_range[0], L_range[1] ) + var x = mix( norm(Lab[1], a_range[0], a_range[1]), 0, ww ) + var y = mix( norm(Lab[2], b_range[0], b_range[1]), 0, hh ) + // move the cursor + this.setLab(Lab) + return Lab + } + this.paint = function() { + val = clamp(val, L_range[0], L_range[1]) + var x, y, t + for (var i = 0; i < w; i++) { + for (var j = 0; j < h; j++) { + x = mix( i/ww, a_range[0], a_range[1] ) + y = mix( j/hh, b_range[0], b_range[1] ) + t = (j*w + i) * 4 + rgb = xyz2rgb(hunterlab2xyz(val, x, y)) + data[t] = Math.round( rgb[0] ) + data[t+1] = Math.round( rgb[1] ) + data[t+2] = Math.round( rgb[2] ) + data[t+3] = 255 + } + } + ctx.putImageData(imageData,0,0) + } + + function hunterlab2xyz (L,a,b) { + var_Y = L / 10 + var_X = a / 17.5 * L / 10 + var_Z = b / 7 * L / 10 + + Y = Math.pow(var_Y, 2) + X = ( var_X + Y ) / 1.02 + Z = -( var_Z - Y ) / 0.847 + xyz = [X,Y,Z] + } + function xyz2rgb(){ + var var_X = xyz[0] / 100 //X from 0 to 95.047 (Observer = 2°, Illuminant = D65) + var var_Y = xyz[1] / 100 //Y from 0 to 100.000 + var var_Z = xyz[2] / 100 //Z from 0 to 108.883 + + var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986 + var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415 + var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570 + + if ( var_R > 0.0031308 ) var_R = 1.055 * Math.pow( var_R, 1 / 2.4 ) - 0.055 + else var_R = 12.92 * var_R + if ( var_G > 0.0031308 ) var_G = 1.055 * Math.pow( var_G, 1 / 2.4 ) - 0.055 + else var_G = 12.92 * var_G + if ( var_B > 0.0031308 ) var_B = 1.055 * Math.pow( var_B, 1 / 2.4 ) - 0.055 + else var_B = 12.92 * var_B + + rgb[0] = clamp(var_R * 255, 0, 255) + rgb[1] = clamp(var_G * 255, 0, 255) + rgb[2] = clamp(var_B * 255, 0, 255) + return rgb + } + function rgb2xyz(RGB){ + var var_R = ( RGB[0] / 255 ) // R from 0 to 255 + var var_G = ( RGB[1] / 255 ) // G from 0 to 255 + var var_B = ( RGB[2] / 255 ) // B from 0 to 255 + + if ( var_R > 0.04045 ) var_R = ( ( var_R + 0.055 ) / 1.055 ) ^ 2.4 + else var_R = var_R / 12.92 + if ( var_G > 0.04045 ) var_G = ( ( var_G + 0.055 ) / 1.055 ) ^ 2.4 + else var_G = var_G / 12.92 + if ( var_B > 0.04045 ) var_B = ( ( var_B + 0.055 ) / 1.055 ) ^ 2.4 + else var_B = var_B / 12.92 + + var_R = var_R * 100 + var_G = var_G * 100 + var_B = var_B * 100 + + //Observer. = 2°, Illuminant = D65 + var x = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805 + var y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722 + var z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505 + return [x,y,z] + } + function xyz2hunterlab (XYZ) { + var X = XYZ[0] + var Y = XYZ[1] || 1e-6 // otherwise divide-by-zero error when converting rgb(0,0,0) + var Z = XYZ[2] + var L = 10 * sqrt( Y ) + var a = 17.5 * ( ( ( 1.02 * X ) - Y ) / sqrt( Y ) ) + var b = 7 * ( ( Y - ( 0.847 * Z ) ) / sqrt( Y ) ) + return [L,a,b] + } +} diff --git a/public/assets/javascripts/ui/editor/MediaEditor.js b/public/assets/javascripts/ui/editor/MediaEditor.js index cc924da..750cf41 100644 --- a/public/assets/javascripts/ui/editor/MediaEditor.js +++ b/public/assets/javascripts/ui/editor/MediaEditor.js @@ -116,17 +116,18 @@ var MediaEditor = FormView.extend({ }, setDimensions: function(){ - this.$width.unitVal( Number(this.scenery.dimensions.a * this.scenery.scale) || "" ) - this.$height.unitVal( Number(this.scenery.dimensions.b * this.scenery.scale) || "" ) + if (! this.scenery) return + this.$width.unitVal( Number(this.scenery.naturalDimensions.a * this.scenery.scale) || "" ) + this.$height.unitVal( Number(this.scenery.naturalDimensions.b * this.scenery.scale) || "" ) }, changeWidth: function(e){ e.stopPropagation() - this.scenery.set_scale( this.$width.unitVal() / this.scenery.dimensions.a ) + this.scenery.set_scale( this.$width.unitVal() / this.scenery.naturalDimensions.a ) this.setDimensions() }, changeHeight: function(e){ e.stopPropagation() - this.scenery.set_scale( this.$height.unitVal() / this.scenery.dimensions.b ) + this.scenery.set_scale( this.$height.unitVal() / this.scenery.naturalDimensions.b ) this.setDimensions() }, changeUnits: function(){ @@ -137,11 +138,13 @@ var MediaEditor = FormView.extend({ bind: function(scenery){ this.scenery = scenery this.scenery.mx.bound = true + this.scenery.mx.el.classList.add("picked") }, unbind: function(){ if (this.scenery && this.scenery.mx) { this.scenery.mx.bound = false + this.scenery.mx.el.classList.remove("picked") } this.scenery = null }, diff --git a/public/assets/javascripts/ui/editor/MediaUpload.js b/public/assets/javascripts/ui/editor/MediaUpload.js index 86bf767..92cf2bd 100644 --- a/public/assets/javascripts/ui/editor/MediaUpload.js +++ b/public/assets/javascripts/ui/editor/MediaUpload.js @@ -1,5 +1,5 @@ -var MediaUpload = View.extend({ +var MediaUpload = UploadView.extend({ el: ".fileUpload", createAction: "/api/media/new", @@ -7,16 +7,12 @@ var MediaUpload = View.extend({ events: { "keydown .url": "enterSubmit", - "change .file": "handleFileSelect", - "submit form": "preventDefault", }, initialize: function(opt){ + this.__super__.initialize.call(this) this.parent = opt.parent - this.$csrf = this.$("[name=_csrf]") this.$url = this.$(".url") - this.$file = this.$(".file") - this.$upload = this.$(".upload-icon") }, show: function(){ @@ -45,7 +41,7 @@ var MediaUpload = View.extend({ return } - media._csrf = this.$csrf.val() + media._csrf = $("[name=_csrf]").val() console.log(media) var request = $.ajax({ @@ -57,68 +53,13 @@ var MediaUpload = View.extend({ }.bind(this)) }, - handleFileSelect: function(e) { - e.stopPropagation(); - e.preventDefault(); - - this.parent.mediaViewer.deleteArmed(false) - - var files = e.dataTransfer ? e.dataTransfer.files : e.target.files; - - for (var i = 0, f; f = files[i]; i++) { - if ( ! f.type.match('image.*')) { - continue; - } - - this.getImageDimensions(f) - } - }, - - getImageDimensions: function(f){ - var base = this - - this.$upload.addClass('uploading') - - var reader = new FileReader(); - - reader.onload = function(e) { - var image = new Image() - image.onload = function(){ - var width = image.naturalWidth, - height = image.naturalHeight - base.upload(f, width, height) - } - image.src = e.target.result - } - - reader.readAsDataURL(f); - }, - - upload: function(f, width, height){ - var fd = new FormData() - fd.append('image', f) - fd.append('width', width) - fd.append('height', height) - fd.append('_csrf', this.$csrf.val()) - - var request = $.ajax({ - url: this.uploadAction, - type: "post", - data: fd, - dataType: "json", - processData: false, - contentType: false, - }) - request.done(this.add.bind(this)) - }, - add: function(media){ console.log(media) - if (media.error) { - return - } - this.$upload.removeClass('uploading') this.parent.mediaViewer.add(media) + }, + + beforeUpload: function(){ + this.parent.mediaViewer.deleteArmed(false) } }) diff --git a/public/assets/javascripts/ui/editor/MediaViewer.js b/public/assets/javascripts/ui/editor/MediaViewer.js index 40bfe80..7cfa863 100644 --- a/public/assets/javascripts/ui/editor/MediaViewer.js +++ b/public/assets/javascripts/ui/editor/MediaViewer.js @@ -2,6 +2,7 @@ var MediaViewer = ModalView.extend({ el: ".mediaDrawer.mediaViewer", destroyAction: "/api/media/destroy", + usesFileUpload: true, events: { 'click .foundToggle': "foundToggle", diff --git a/public/assets/javascripts/ui/editor/WallpaperPicker.js b/public/assets/javascripts/ui/editor/WallpaperPicker.js index 9ee441b..61ecb78 100644 --- a/public/assets/javascripts/ui/editor/WallpaperPicker.js +++ b/public/assets/javascripts/ui/editor/WallpaperPicker.js @@ -1,40 +1,70 @@ -var WallpaperPicker = View.extend({ +var WallpaperPicker = UploadView.extend({ el: ".wallpaper", + mediaTag: "wallpaper", + uploadAction: "/api/media/upload", + events: { "click .swatch": 'pick', - }, + }, initialize: function(){ - var wm = new WallpaperManager() - app.on('wallpaper-ready', function(){ - var black = [0,0,0,1.0] - var white = [255,255,255,1.0] - var swatches = wm.buildSwatches(black, white, 2) - swatches.forEach(function(swatch){ - var dataUrl = swatch.toDataURL() - var span = document.createElement('span') - span.style.backgroundImage = "url(" + dataUrl + ")" - span.className = "swatch" - this.$el.append(span) - }.bind(this)) - }.bind(this)) - wm.init() + this.__super__.initialize.call(this) + this.$swatches = this.$(".swatches") + }, + + loaded: false, + show: function(){ + if (! this.loaded) { + this.load() + } + else { + this.toggle(true) + } + }, + + hide: function(){ + this.__super__.hide.call(this) }, - toggle: function(state){ - this.$el.toggleClass("active", state); - // toggle the class that makes the cursor a paintbucket - // $("body").removeClass("pastePaper"); + load: function(){ + $.get("/api/media/user", { tag: this.mediaTag }, this.populate.bind(this)) }, - show: function(){ - this.toggle(true) + + populate: function(data){ + console.log(data) + this.loaded = true + data && data.forEach(this.add.bind(this)) + this.toggle(true) + }, + + add: function (media) { + if (media.type !== "image") { return } + var swatch = document.createElement("div") + swatch.className = "swatch" + swatch.style.backgroundImage = "url(" + media.url + ")" + this.$swatches.append(swatch) + }, + + toggle: function (state) { + if (state && ! this.loaded) { + this.show() + } + else { + this.$el.toggleClass("active", state) + } + // toggle the class that makes the cursor a paintbucket + // $("body").removeClass("pastePaper") }, + hide: function(){ this.toggle(false) }, + beforeUpload: function(){ + }, + pick: function(e){ var $swatch = $(e.currentTarget) var $floatingSwatch = $(".floatingSwatch") @@ -58,94 +88,6 @@ var WallpaperPicker = View.extend({ $floatingSwatch.show() _followCursor(e); }) - } + }, }) - -// pattern -// scale -// foreground -// background - -var WallpaperManager = function () { - - var image = new Image () - var imageData - var w, h - - this.masks = [] - - this.init = function(){ - this.load() - } - - this.load = function(){ - image.onload = function(){ - this.loadImageData() - this.buildMasks() - app.tube('wallpaper-ready') - }.bind(this) - - image.src = "/assets/img/palette.gif" - } - - this.loadImageData = function(){ - var canvas = document.createElement('canvas') - var ctx = canvas.getContext('2d') - w = canvas.width = image.naturalWidth - h = canvas.height = image.naturalHeight - ctx.drawImage(image, 0,0) - imageData = ctx.getImageData(0,0,image.naturalWidth,image.naturalHeight).data - } - - this.buildMasks = function(){ - var mask - for (var y = 0; y < 6; y++) { - for (var x = 0; x < 16; x++) { - mask = this.buildMask(x,y) - this.masks.push(mask) - } - } - } - - this.buildMask = function(x,y){ - // add the offset of the top-left swatch - x = (x * 18) + 15 - y = (y * 16) + 5 - - var mask = new Array(64) - var t = 0 - for (var i = 0; i < 8; i++) { - for (var j = 0; j < 8; j++) { - t = ( w*(y+j) + x+i ) * 4 - mask[j*8+i] = imageData[t] === 0 - } - } - return mask - } - - this.buildSwatches = function(black, white, scale) { - var swatches = this.masks.map(function(mask){ - return this.buildSwatch(mask,black,white,scale) - }.bind(this)) - - return swatches - } - - this.buildSwatch = function(mask,black,white,scale){ - black = 'rgba(' + black.join(',') + ')' - white = 'rgba(' + white.join(',') + ')' - var canvas = document.createElement("canvas") - canvas.width = 8*scale - canvas.height = 8*scale - var ctx = canvas.getContext('2d') - for (var i = 0; i < 8; i++) { - for (var j = 0; j < 8; j++) { - ctx.fillStyle = mask[j*8+i] ? black : white - ctx.fillRect(i*scale, j*scale, scale, scale) - } - } - return canvas - } - -}
\ No newline at end of file diff --git a/public/assets/javascripts/ui/lib/FormView.js b/public/assets/javascripts/ui/lib/FormView.js index ab33bc0..17b748a 100644 --- a/public/assets/javascripts/ui/lib/FormView.js +++ b/public/assets/javascripts/ui/lib/FormView.js @@ -33,7 +33,7 @@ var FormView = View.extend({ }, serialize: function(){ - var fd = new FormData() + var fd = new FormData(), hasCSRF = false this.$("input[name], select[name], textarea[name]").each( function(){ if (this.type == "file") { @@ -48,9 +48,14 @@ var FormView = View.extend({ } else { fd.append(this.name, this.value); + hasCSRF = hasCSRF || this.name == "_csrf" } }); + if (! hasCSRF) { + fd.append("_csrf", $("[name=_csrf]").val()) + } + return fd }, @@ -71,9 +76,12 @@ var FormView = View.extend({ return } } + + var action = typeof this.action == "function" ? this.action() : this.action + if (! action) return var request = $.ajax({ - url: this.action, + url: action, type: this.method, data: this.serialize(), dataType: "json", diff --git a/public/assets/javascripts/ui/lib/ModalView.js b/public/assets/javascripts/ui/lib/ModalView.js index 937c1e9..d9b518a 100644 --- a/public/assets/javascripts/ui/lib/ModalView.js +++ b/public/assets/javascripts/ui/lib/ModalView.js @@ -10,9 +10,15 @@ var ModalView = View.extend({ } }, + usesFileUpload: false, + show: function(){ $(".mediaDrawer").removeClass("active") - $(".fileUpload").removeClass("active") + + if (! this.usesFileUpload) { + $(".fileUpload").removeClass("active") + } + this.$el.addClass("active") $("body").addClass("noOverflow") }, @@ -32,4 +38,5 @@ var ModalView = View.extend({ this.hide() } } + }) diff --git a/public/assets/javascripts/ui/lib/UploadView.js b/public/assets/javascripts/ui/lib/UploadView.js new file mode 100644 index 0000000..efaa8c9 --- /dev/null +++ b/public/assets/javascripts/ui/lib/UploadView.js @@ -0,0 +1,90 @@ + +var UploadView = View.extend({ + + // define uploadAction + + events: { + "change .file": "handleFileSelect", + "submit form": "preventDefault", + }, + + initialize: function(){ + this.$file = this.$(".file") + this.$upload = this.$(".upload-icon") + }, + + beforeUpload: function(){ + }, + + handleFileSelect: function(e) { + e.stopPropagation(); + e.preventDefault(); + + this.beforeUpload() + + var files = e.dataTransfer ? e.dataTransfer.files : e.target.files; + + for (var i = 0, f; f = files[i]; i++) { + if ( ! f.type.match('image.*')) { + continue; + } + + this.getImageDimensions(f) + } + }, + + getImageDimensions: function(f){ + var base = this + + this.$upload.addClass('uploading') + + var reader = new FileReader(); + + reader.onload = function(e) { + var image = new Image() + image.onload = function(){ + var width = image.naturalWidth, + height = image.naturalHeight + base.upload(f, width, height) + } + image.src = e.target.result + } + + reader.readAsDataURL(f); + }, + + upload: function(f, width, height){ + var fd = new FormData() + fd.append('image', f) + fd.append('width', width) + fd.append('height', height) + fd.append('_csrf', $("[name=_csrf]").val()) + + if (this.mediaTag) { + fd.append('tag', this.mediaTag) + } + + var request = $.ajax({ + url: this.uploadAction, + type: "post", + data: fd, + dataType: "json", + processData: false, + contentType: false, + }) + request.done(this.success.bind(this)) + }, + + success: function(media){ + if (media.error) { + return + } + this.$upload.removeClass('uploading') + this.add(media) + }, + + add: function(media){ + console.log(media) + }, + +}) diff --git a/public/assets/javascripts/ui/site/SignUpModal.js b/public/assets/javascripts/ui/site/SignUpModal.js index 5c651ee..a452023 100644 --- a/public/assets/javascripts/ui/site/SignUpModal.js +++ b/public/assets/javascripts/ui/site/SignUpModal.js @@ -34,4 +34,3 @@ var SignUpModal = ModalFormView.extend({ } }) - diff --git a/public/assets/javascripts/util.js b/public/assets/javascripts/util.js index b92dcf3..7812a4d 100644 --- a/public/assets/javascripts/util.js +++ b/public/assets/javascripts/util.js @@ -12,7 +12,10 @@ function sanitize (s){ return (s || "").replace(new RegExp("[<>&]", 'g'), "") } function capitalize (s){ return s.split(" ").map(capitalizeWord).join(" ") } function capitalizeWord (s){ return s.charAt(0).toUpperCase() + s.slice(1) } function slugify (s){ return (s || "").toLowerCase().replace(/\s/g,"-").replace(/[^-_a-zA-Z0-9]/g, '-').replace(/-+/g,"-") } - +function rgb_string (rgb) { return "rgb(" + rgb.map(Math.round).join(",") + ")" } +function rgba_string (rgb,a) { return "rgba(" + rgb.map(Math.round).join(",") + "," + a + ")" } +function hex_string (rgb) { return "#" + rgb.map(Math.round).map(function(n){ var s = n.toString(16); return s.length == 1 ? "0"+s : s }).join("") } +function parse_rgba_string (s) { return s.match(/(\d+)/g).slice(0,3) } var E = Math.E var PI = Math.PI |
