diff options
| author | Jules Laplace <jules@okfoc.us> | 2014-08-13 17:56:31 -0400 |
|---|---|---|
| committer | Jules Laplace <jules@okfoc.us> | 2014-08-13 17:56:31 -0400 |
| commit | c4c45b64c2c0fc109f4c21effe7f73f5c46a1ae9 (patch) | |
| tree | f6e5e703af41a53fe3a9daa3de23e671a1c6ab74 | |
| parent | 466ccfdccd2d761f31ba78a74a40544b77b358e5 (diff) | |
| parent | b7b881a00a9b73ba54cc3a62edc402a903ec9142 (diff) | |
merge
50 files changed, 955 insertions, 208 deletions
diff --git a/public/assets/img/destroy-cursor.png b/public/assets/img/destroy-cursor.png Binary files differnew file mode 100644 index 0000000..eebe3cd --- /dev/null +++ b/public/assets/img/destroy-cursor.png diff --git a/public/assets/javascripts/app.js b/public/assets/javascripts/app.js index 1419d1d..ad3c601 100644 --- a/public/assets/javascripts/app.js +++ b/public/assets/javascripts/app.js @@ -27,11 +27,7 @@ app.init = function () { } app.launch = function () { - var mainbox, - coords, - box, size, - floor, - movements + var movements scene = new MX.Scene().addTo('#scene') scene.width = window.innerWidth @@ -50,14 +46,19 @@ app.launch = function () { if (MX.Map) map = app.map = new MX.Map() - movements = app.movements = new MX.Movements(cam, viewHeight) - movements.init() + if (is_mobile) { + app.movements = new MX.MobileMovements(cam, viewHeight) + } + else { + app.movements = new MX.Movements(cam, viewHeight) + } + app.movements.init() function animate (t) { requestAnimationFrame(animate) environment.update(t) window.path && path.update(t) - movements.update() + app.movements.update() scene.update() } diff --git a/public/assets/javascripts/mx/extensions/mx.movements.js b/public/assets/javascripts/mx/extensions/mx.movements.js index 191088f..3b7d3e2 100644 --- a/public/assets/javascripts/mx/extensions/mx.movements.js +++ b/public/assets/javascripts/mx/extensions/mx.movements.js @@ -149,7 +149,8 @@ MX.Movements = function (cam) { case 32: // space moveUp = moveDown = false break - + +/* case 48: // 0 cam.rotationX = 0 cam.rotationY = 0 @@ -157,6 +158,7 @@ MX.Movements = function (cam) { cam.y = viewHeight cam.z = 0 break +*/ } }) diff --git a/public/assets/javascripts/mx/extensions/mx.movementsMobile.js b/public/assets/javascripts/mx/extensions/mx.movementsMobile.js new file mode 100644 index 0000000..994c8d7 --- /dev/null +++ b/public/assets/javascripts/mx/extensions/mx.movementsMobile.js @@ -0,0 +1,124 @@ + +MX.MobileMovements = function (cam) { + + var touching = true, + moving = false, + startTime = null, + v = 12, + vr = Math.PI * 0.012, + vx = vy = vz = 0; + + var directionLocked = false, + directionLockThreshold = 5 + + var pos = { x: 0, y: viewHeight, z: 0, rotationX: 0, rotationY: 0 } + + var pointX, pointY, deltaX, deltaY, distX = 0, distY = 0, absDistX = 0, absDistY = 0, startTime + + return { + + init: function () { + document.addEventListener("touchstart", function(e){ + if (e.touches.length == 1) { + touching = true + + startTime = Date.now() + + var point = event.touches[0] + pointX = point.pageX + pointY = point.pageY + distX = distY = 0 + pos.x = cam.x + pos.z = cam.z + pos.rotationY = cam.rotationY + } + }) + document.addEventListener("touchmove", function(e){ + e.preventDefault() + if (e.touches.length == 1) { + + var timestamp = Date.now() + var point = event.touches[0] + deltaX = point.pageX - pointX + deltaY = point.pageY - pointY + + pointX = point.pageX + pointY = point.pageY + + distX += deltaX + distY += deltaY + absDistX = abs(distX) + absDistY = abs(distY) + } + }) + document.addEventListener("touchend", function(e){ + e.preventDefault() + if (e.touches.length == 0) { + touching = directionLocked = false + var timestamp = Date.now() + var duration = startTime - timestamp + if (duration < 300) { + } + } + }) + }, + + update: function () { + if (distX || distY) { + var oldDistY = absDistY, oldDistX = absDistX + absDistY = avg(absDistY, 0, 5) + var dy = (oldDistY - absDistY) * sign(distY) * 2 + + absDistX = avg(absDistX, 0, 5) + var dx = (oldDistX - absDistX) * sign(distX) * 2 + + distY = sign(distY) * absDistY + distX = sign(distX) * absDistX + + pos.x -= dy * Math.cos(pos.rotationY + Math.PI / 2) + pos.z -= dy * Math.sin(pos.rotationY + Math.PI / 2) + pos.rotationY += dx / (window.innerWidth) * Math.PI / 2 + cam.rotationY = pos.rotationY + + app.tube("move", pos) + } + }, + + lock: function(){ locked = true }, + unlock: function(){ locked = false }, + scale: function(n){ if (n) scale = n; return scale }, + resetScale: function(n){ scale = DEFAULT_SCALE }, + gravity: function(g){ return typeof g == "boolean" ? gravity = g : gravity }, + velocity: function(n){ v = clamp(n, 1, 50) }, + jumpVelocity: function(n){ jumpV = clamp(n, 1, 50) }, + } + +} + + +// function momentum (current, start, time, lowerMargin, wrapperSize, deceleration) { +// var distance = current - start, +// speed = Math.abs(distance) / time, +// destination, +// duration; +// +// deceleration = deceleration === undefined ? 0.0006 : deceleration; +// +// destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 ); +// duration = speed / deceleration; +// +// if ( destination < lowerMargin ) { +// destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin; +// distance = Math.abs(destination - current); +// duration = distance / speed; +// } else if ( destination > 0 ) { +// destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0; +// distance = Math.abs(current) + destination; +// duration = distance / speed; +// } +// +// return { +// destination: Math.round(destination), +// duration: duration +// }; +// } diff --git a/public/assets/javascripts/mx/primitives/mx.image.js b/public/assets/javascripts/mx/primitives/mx.image.js index 278fa1e..a640620 100644 --- a/public/assets/javascripts/mx/primitives/mx.image.js +++ b/public/assets/javascripts/mx/primitives/mx.image.js @@ -14,6 +14,7 @@ MX.Image = MX.Object3D.extend({ ops.className && this.el.classList.add(ops.className) this.backface && this.el.classList.add("backface-visible") this.el.classList.add("image") + this.el.classList.add("mx-scenery") this.el.style.backgroundRepeat = 'no-repeat' diff --git a/public/assets/javascripts/mx/primitives/mx.video.js b/public/assets/javascripts/mx/primitives/mx.video.js index cdb92c8..12d3dcb 100644 --- a/public/assets/javascripts/mx/primitives/mx.video.js +++ b/public/assets/javascripts/mx/primitives/mx.video.js @@ -19,10 +19,9 @@ MX.Video = MX.Object3D.extend({ ops.className && this.el.classList.add(ops.className) this.backface && this.el.classList.add("backface-visible") this.el.classList.add("video") + this.el.classList.add("mx-scenery") this.paused = !! this.media.autoplay this.muted = app.muted || !! this.media.mute - - this.load() }, load: function(ops){ diff --git a/public/assets/javascripts/mx/primitives/mx.vimeo.js b/public/assets/javascripts/mx/primitives/mx.vimeo.js index 64d9103..4922519 100644 --- a/public/assets/javascripts/mx/primitives/mx.vimeo.js +++ b/public/assets/javascripts/mx/primitives/mx.vimeo.js @@ -19,11 +19,10 @@ MX.Vimeo = MX.Object3D.extend({ ops.className && this.el.classList.add(ops.className) this.backface && this.el.classList.add("backface-visible") this.el.classList.add("video") + this.el.classList.add("mx-scenery") this.paused = !! this.media.autoplay this.muted = app.muted || !! this.media.mute this.started = false - - this.load() }, load: function (ops) { @@ -41,23 +40,24 @@ MX.Vimeo = MX.Object3D.extend({ this.el.appendChild(preload) this.player = $f(preload) - this.player.addEvent('ready', this.ready.bind(this)) + this.player.addEvent('ready', $.proxy(this.ready, this)) }, ready: function(){ console.log("vimeo ready") + this.started = true // wait until ready before binding events. other events: play, pause - this.player.addEvent('play', this.onPlay.bind(this)) - this.player.addEvent('pause', this.onPause.bind(this)) - this.player.addEvent('finish', this.finished.bind(this)) + this.player.addEvent('play', $.proxy(this.onPlay, this)) + this.player.addEvent('pause', $.proxy(this.onPause, this)) + this.player.addEvent('finish', $.proxy(this.finished, this)) // this is async on vimeo so call it asap - this.player.api('getDuration', function(n){ + this.player.api('getDuration', $.proxy(function(n){ console.log("vimeo duration", n) this.player.duration = n - }.bind(this)) + }, this)) if (this.media.mute) { this.mute() @@ -90,9 +90,9 @@ MX.Vimeo = MX.Object3D.extend({ seek: function(n){ // defer seek until we have duration if (! this.duration()) { - setTimeout(function(){ + setTimeout($.proxy(function(){ this.seek(n) - }.bind(this), 300) + }, this), 300) return } @@ -108,9 +108,9 @@ MX.Vimeo = MX.Object3D.extend({ this.paused = false this.play() this.pause() - setTimeout(function(){ + setTimeout($.proxy(function(){ this.pause() - }.bind(this), 100) + }, this), 100) } }, diff --git a/public/assets/javascripts/mx/primitives/mx.youtube.js b/public/assets/javascripts/mx/primitives/mx.youtube.js index f7f00aa..873348f 100644 --- a/public/assets/javascripts/mx/primitives/mx.youtube.js +++ b/public/assets/javascripts/mx/primitives/mx.youtube.js @@ -19,10 +19,9 @@ MX.Youtube = MX.Object3D.extend({ ops.className && this.el.classList.add(ops.className) this.backface && this.el.classList.add("backface-visible") this.el.classList.add("video") + this.el.classList.add("mx-scenery") this.paused = !! this.media.autoplay this.muted = app.muted || !! this.media.mute - - this.load() }, load: function (ops) { diff --git a/public/assets/javascripts/rectangles/_env.js b/public/assets/javascripts/rectangles/_env.js index 3cfe969..4b14a21 100644 --- a/public/assets/javascripts/rectangles/_env.js +++ b/public/assets/javascripts/rectangles/_env.js @@ -35,6 +35,20 @@ environment.init = function(){ zoom: -4.8 }) } + + keys.on("z", function(e){ + e.preventDefault() + if (e.ctrlKey || e.metaKey) { + if (e.shiftKey) { + var canRedo = UndoStack.redo() + console.log("can redo", canRedo) + } + else { + var canUndo = UndoStack.undo() + console.log("can undo", canUndo) + } + } + }) } environment.update = function(t){ map.update() diff --git a/public/assets/javascripts/rectangles/engine/map/draw.js b/public/assets/javascripts/rectangles/engine/map/draw.js index 8e1fe5a..3e185d2 100644 --- a/public/assets/javascripts/rectangles/engine/map/draw.js +++ b/public/assets/javascripts/rectangles/engine/map/draw.js @@ -29,7 +29,7 @@ Map.Draw = function(map, opt){ ctx.translate( map.center.a, map.center.b ) ctx.scale( -1, 1 ) - draw.regions(Rooms.regions, colors) + draw.regions(Rooms.regions, [ "#f8f8f8" ]) draw.mouse(map.ui.mouse.cursor) draw.coords() scene && draw.camera(scene.camera) @@ -55,7 +55,7 @@ Map.Draw = function(map, opt){ } draw.clear = function(){ - ctx.fillStyle = "rgba(255,255,255,0.9)" + ctx.fillStyle = "rgba(255,255,255,0.98)" ctx.clearRect(0, 0, map.dimensions.a, map.dimensions.b) ctx.fillRect(0, 0, map.dimensions.a, map.dimensions.b) } diff --git a/public/assets/javascripts/rectangles/engine/map/ui_editor.js b/public/assets/javascripts/rectangles/engine/map/ui_editor.js index 577ea32..9a557b9 100644 --- a/public/assets/javascripts/rectangles/engine/map/ui_editor.js +++ b/public/assets/javascripts/rectangles/engine/map/ui_editor.js @@ -30,6 +30,7 @@ Map.UI.Editor = function(map){ // function down (e, cursor){ + var room cursor.x.div(map.dimensions.a).add(0.5).mul(map.dimensions.a / map.zoom).add(map.center.a) cursor.y.div(map.dimensions.b).sub(0.5).mul(map.dimensions.b / map.zoom).sub(map.center.b) @@ -52,22 +53,28 @@ Map.UI.Editor = function(map){ if (intersects.length && base.permissions.destroy) { base.mouse.down = false - Rooms.remove(intersects[0]) - app.tube("builder-destroy-room", intersects[0]) + + room = intersects[0] + + UndoStack.push({ + type: "destroy-room", + undo: room.copy(), + redo: { id: room.id }, + }) + + Rooms.remove(room) + app.tube("builder-destroy-room", room) return } - else if (intersects.length && (base.permissions.move || base.permissions.resize)) { + else if (intersects.length) { base.dragging = intersects[0] - base.resizing = base.permissions.resize && base.dragging.rect.nearEdge(cursor.x.a, cursor.y.a, resize_margin / map.zoom) + base.resizing = base.dragging.rect.nearEdge(cursor.x.a, cursor.y.a, resize_margin / map.zoom) base.dragging.rect.translation.sides = base.resizing app.tube("builder-pick-room", intersects[0]) } else if (base.permissions.create) { base.creating = true } - else if (intersects.length) { - app.tube("builder-pick-room", intersects[0]) - } if (e.shiftKey && base.dragging) { base.dragging.rect.quantize(10/map.zoom) @@ -77,6 +84,45 @@ Map.UI.Editor = function(map){ function move (e, cursor) { cursor.x.div(map.dimensions.a).add(0.5).mul(map.dimensions.a / map.zoom).add(map.center.a) cursor.y.div(map.dimensions.b).sub(0.5).mul(map.dimensions.b / map.zoom).sub(map.center.b) + + var intersects = Rooms.filter(function(r){ + return r.rect.contains(cursor.x.a, cursor.y.a) + }) + + if (base.permissions.destroy) { + map.el.className = "destroy" + } + else if (intersects.length) { + var edges = intersects[0].rect.nearEdge(cursor.x.a, cursor.y.a, resize_margin / map.zoom) + switch (edges) { + case FRONT_LEFT: + case BACK_RIGHT: + map.el.className = "nesw-resize" + break + + case FRONT_RIGHT: + case BACK_LEFT: + map.el.className = "nwse-resize" + break + + case FRONT: + case BACK: + map.el.className = "ns-resize" + break + + case LEFT: + case RIGHT: + map.el.className = "ew-resize" + break + + default: + map.el.className = "move" + break + } + } + else { + map.el.className = "" + } } function drag (e, cursor) { @@ -115,20 +161,40 @@ Map.UI.Editor = function(map){ cursor.x.abs().quantize(1) cursor.y.abs().quantize(1) var room = Rooms.add_with_rect( cursor ) + + UndoStack.push({ + type: "create-room", + undo: { id: room.id }, + redo: room.copy() + }) + app.tube("builder-pick-room", room) } } - if (base.resizing) { - base.dragging.rect.resize() - } - else if (base.dragging) { - base.dragging.rect.translate() - } + if (base.resizing || base.dragging) { + var oldState = base.dragging.copy() + + if (base.resizing) { + base.dragging.rect.resize() + } + else if (base.dragging) { + base.dragging.rect.translate() + } + + UndoStack.push({ + type: "update-room", + undo: oldState, + redo: base.dragging.copy() + }) + } + base.creating = base.dragging = base.resizing = false } + var wheelState, wheelTimeout + function mousewheel (e, val, delta){ var cursor = base.mouse.cursor @@ -137,8 +203,20 @@ Map.UI.Editor = function(map){ }) if (intersects.length) { + wheelState = wheelState || intersects[0].copy() + intersects[0].height = clamp( ~~(intersects[0].height - delta), height_min, height_max ) - Rooms.clipper.update() + + clearTimeout(wheelTimeout) + wheelTimeout = setTimeout(function(){ + UndoStack.push({ + type: "update-room", + undo: wheelState, + redo: intersects[0].copy() + }) + Rooms.clipper.update() + wheelState = null + }, 500) } else { map.set_zoom(map.zoom_exponent - delta/20) diff --git a/public/assets/javascripts/rectangles/engine/scenery/_scenery.js b/public/assets/javascripts/rectangles/engine/scenery/_scenery.js index 137c74a..c43ef14 100644 --- a/public/assets/javascripts/rectangles/engine/scenery/_scenery.js +++ b/public/assets/javascripts/rectangles/engine/scenery/_scenery.js @@ -36,8 +36,13 @@ var Scenery = new function(){ mx: mx }) base.nextMedia = null + return media } - + + base.find = function(id){ + return base.list[id] || null + } + base.remove = function(id){ var media = base.list[id] delete base.list[id] diff --git a/public/assets/javascripts/rectangles/engine/scenery/move.js b/public/assets/javascripts/rectangles/engine/scenery/move.js index cc5b014..fa247e1 100644 --- a/public/assets/javascripts/rectangles/engine/scenery/move.js +++ b/public/assets/javascripts/rectangles/engine/scenery/move.js @@ -3,6 +3,7 @@ Scenery.move = function(base){ var x, y, z, bounds var dragging = false + var oldState this.bind = function(){ Scenery.mouse.bind_el(base.mx.el) @@ -23,6 +24,12 @@ Scenery.move = function(base){ function down (e, cursor){ if (e.target != base.mx.el) return; if (editor.permissions.destroy) { + UndoStack.push({ + type: 'destroy-scenery', + undo: base.serialize(), + redo: { id: base.id }, + }) + Scenery.remove(base.id) return } @@ -39,6 +46,7 @@ Scenery.move = function(base){ y = base.mx.y z = base.mx.z bounds = base.bounds + oldState = base.serialize() document.body.classList.add("dragging") } @@ -63,8 +71,18 @@ Scenery.move = function(base){ } function up (e, cursor){ + if (! dragging || ! oldState) return + dragging = false document.body.classList.remove("dragging") + + UndoStack.push({ + type: 'update-scenery', + undo: oldState, + redo: base.serialize(), + }) + + oldState = null } function switch_wall (e, new_wall, cursor){ diff --git a/public/assets/javascripts/rectangles/engine/scenery/resize.js b/public/assets/javascripts/rectangles/engine/scenery/resize.js index df058bb..c5c754a 100644 --- a/public/assets/javascripts/rectangles/engine/scenery/resize.js +++ b/public/assets/javascripts/rectangles/engine/scenery/resize.js @@ -7,6 +7,7 @@ Scenery.resize = new function(){ var x, y, z, bounds var dragging = false var dimensions, position, scale + var oldState var dots = [], dot, selected_dot @@ -54,7 +55,7 @@ Scenery.resize = new function(){ } // move all the dots to the object's current position - base.move_dots = function(){ + base.move_dots = function(){ x = obj.mx.x + sin(rotationY) * dot_distance_from_picture y = obj.mx.y z = obj.mx.z - cos(rotationY) * dot_distance_from_picture @@ -88,7 +89,7 @@ Scenery.resize = new function(){ // pick a new object to focus on and show the dots base.show = function(new_object) { - if (obj === new_object) return + // if (obj === new_object) return obj = new_object base.add_dots() @@ -151,6 +152,7 @@ Scenery.resize = new function(){ dimensions = obj.dimensions position = new vec3(obj.mx.x, obj.mx.y, obj.mx.z) scale = obj.mx.scale + oldState = obj.serialize() document.body.classList.add("dragging") } @@ -191,6 +193,13 @@ Scenery.resize = new function(){ if (! editor.permissions.resize) { return } obj.scale = obj.mx.ops.scale = obj.mx.scale obj.set_wall() + + UndoStack.push({ + type: 'update-scenery', + undo: oldState, + redo: obj.serialize(), + }) + document.body.classList.remove("dragging") } diff --git a/public/assets/javascripts/rectangles/engine/scenery/types/_object.js b/public/assets/javascripts/rectangles/engine/scenery/types/_object.js index aa1fefb..46bc0e7 100644 --- a/public/assets/javascripts/rectangles/engine/scenery/types/_object.js +++ b/public/assets/javascripts/rectangles/engine/scenery/types/_object.js @@ -66,6 +66,10 @@ Scenery.types.base = Fiber.extend(function(base){ this.center = this.wall.center() }, + set_scale: function(scale){ + this.scale = this.mx.scale = this.mx.ops.scale = scale || 1.0 + }, + recenter: function(){ this.mx.move({ x: this.center.a, diff --git a/public/assets/javascripts/rectangles/engine/scenery/types/video.js b/public/assets/javascripts/rectangles/engine/scenery/types/video.js index 79cfb1c..a8df875 100644 --- a/public/assets/javascripts/rectangles/engine/scenery/types/video.js +++ b/public/assets/javascripts/rectangles/engine/scenery/types/video.js @@ -34,7 +34,8 @@ Scenery.types.video = Scenery.types.base.extend(function(base){ y: this.scale * this.media.height/2, backface: false, }) - scene.add( this.mx ) + scene.add(this.mx) + this.mx.load() }, play: function(){ diff --git a/public/assets/javascripts/rectangles/engine/scenery/undo.js b/public/assets/javascripts/rectangles/engine/scenery/undo.js new file mode 100644 index 0000000..7798550 --- /dev/null +++ b/public/assets/javascripts/rectangles/engine/scenery/undo.js @@ -0,0 +1,100 @@ +(function(){ + UndoStack.register([ + { + type: "create-scenery", + undo: function(state){ + Scenery.remove(state.id) + }, + redo: function(state){ + Scenery.deserialize([ state ]) + }, + }, + { + type: "update-scenery", + undo: function(state){ + var scenery = Scenery.find(state.id) + scenery.deserialize(state) + scenery.set_wall(Rooms.walls[ state.wall_id ]) + + if (editor.permissions.resize) { + Scenery.resize.show(scenery) + } + }, + redo: function(state){ + var scenery = Scenery.find(state.id) + scenery.deserialize(state) + scenery.set_wall(Rooms.walls[ state.wall_id ]) + + if (editor.permissions.resize) { + Scenery.resize.show(scenery) + Scenery.resize.rotate_dots() + Scenery.resize.move_dots() + } + }, + }, + { + type: "destroy-scenery", + undo: function(state){ + Scenery.deserialize([ state ]) + }, + redo: function(state){ + Scenery.remove(state.id) + }, + }, + + // + + { + type: "create-room", + undo: function(room){ + Rooms.remove(room) + Rooms.clipper.update() + }, + redo: function(room){ + Rooms.add(new Room(room)) + Rooms.clipper.update() + app.tube("builder-pick-room", 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) + }, + 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) + }, + }, + { + type: "destroy-room", + undo: function(room){ + Rooms.add(new Room(room)) + Rooms.clipper.update() + app.tube("builder-pick-room", room) + }, + redo: function(room){ + Rooms.remove(room) + Rooms.clipper.update() + }, + }, + + // + + { + type: "update-wallpaper", + undo: function(state){ + }, + redo: function(state){ + }, + }, + + ]) +})() diff --git a/public/assets/javascripts/rectangles/models/rect.js b/public/assets/javascripts/rectangles/models/rect.js index 500ee6d..5952f6a 100644 --- a/public/assets/javascripts/rectangles/models/rect.js +++ b/public/assets/javascripts/rectangles/models/rect.js @@ -39,6 +39,10 @@ Rect.prototype.clone = function(){ return new Rect( this.x.clone(), this.y.clone() ) } + Rect.prototype.assign = function(r) { + this.x.assign(r.x) + this.y.assign(r.y) + } Rect.prototype.center = function(){ return new vec2(this.x.midpoint(), this.y.midpoint()) } @@ -110,6 +114,10 @@ Rect.prototype.fits = function(v){ return this.x.length() >= v.a && this.y.length() >= v.b } + Rect.prototype.zero = function(){ + this.a.zero() + this.b.zero() + } Rect.prototype.nearEdge = function (x, y, r) { var edges = 0 if (x < this.x.a+r) { diff --git a/public/assets/javascripts/rectangles/models/room.js b/public/assets/javascripts/rectangles/models/room.js index 937c928..33a94d0 100644 --- a/public/assets/javascripts/rectangles/models/room.js +++ b/public/assets/javascripts/rectangles/models/room.js @@ -40,6 +40,14 @@ this.focused = false } + Room.prototype.copy = function(){ + return { + id: this.id, + rect: this.rect.clone(), + height: this.height, + } + } + Room.prototype.toString = function(){ return this.rect.toString() } diff --git a/public/assets/javascripts/rectangles/models/vec2.js b/public/assets/javascripts/rectangles/models/vec2.js index 2bf286b..214feb9 100644 --- a/public/assets/javascripts/rectangles/models/vec2.js +++ b/public/assets/javascripts/rectangles/models/vec2.js @@ -17,6 +17,10 @@ vec2.prototype.clone = function(){ return new vec2(this.a, this.b) } + vec2.prototype.assign = function(v){ + this.a = v.a + this.b = v.b + } vec2.prototype.abs = function(){ if (this.b < this.a) { this.invert() @@ -55,6 +59,9 @@ this.b /= n return this } + vec2.prototype.zero = function(){ + this.a = this.b = 0 + } vec2.prototype.setPosition = function(n){ var len = this.length() this.a = n diff --git a/public/assets/javascripts/rectangles/models/wall.js b/public/assets/javascripts/rectangles/models/wall.js index fdc91fd..f015a44 100644 --- a/public/assets/javascripts/rectangles/models/wall.js +++ b/public/assets/javascripts/rectangles/models/wall.js @@ -52,7 +52,13 @@ // base.randomize_colors() // console.log(sidesToString(base.side)) if (Scenery.nextMedia) { - Scenery.addNextToWall(base, mx) + var scenery = Scenery.addNextToWall(base) + + UndoStack.push({ + type: 'create-scenery', + undo: { id: scenery.id }, + redo: scenery.serialize(), + }) } else if (Scenery.nextWallpaper) { base.wallpaper() diff --git a/public/assets/javascripts/rectangles/util/constants.js b/public/assets/javascripts/rectangles/util/constants.js index 58cb1a5..b9485ca 100644 --- a/public/assets/javascripts/rectangles/util/constants.js +++ b/public/assets/javascripts/rectangles/util/constants.js @@ -8,6 +8,12 @@ var TOP = CEILING, BOTTOM = FLOOR, BOTTOM_RIGHT = BOTTOM | RIGHT, TOP_BOTTOM = TOP | BOTTOM +var FRONT_LEFT = FRONT | LEFT, + FRONT_RIGHT = FRONT | RIGHT, + BACK_LEFT = BACK | LEFT, + BACK_RIGHT = BACK | RIGHT + + var height_min = 200, height_max = 2000, side_min = 10, diff --git a/public/assets/javascripts/rectangles/util/keys.js b/public/assets/javascripts/rectangles/util/keys.js index 5a5c9d2..62d763f 100644 --- a/public/assets/javascripts/rectangles/util/keys.js +++ b/public/assets/javascripts/rectangles/util/keys.js @@ -19,7 +19,7 @@ var keys = (function(){ break; default: if (keys.debug) console.log(key) - base.tube(key) + base.tube(key, e) break; } }) @@ -158,8 +158,8 @@ var keys = (function(){ 'backslash' : '220', 'closebracket' : '221', 'single_quote' : '222' - } - var KEY_NAMES = invert_hash(KEYCODES) + }, + KEY_NAMES = invert_hash(KEYCODES) return base })()
\ No newline at end of file diff --git a/public/assets/javascripts/rectangles/util/measurement.js b/public/assets/javascripts/rectangles/util/measurement.js new file mode 100644 index 0000000..d6a0b35 --- /dev/null +++ b/public/assets/javascripts/rectangles/util/measurement.js @@ -0,0 +1,82 @@ +$.fn.resetUnitVal = function(){ + this.each(function(){ + var n = $(this).data("px") + $(this).unitVal(n) + }); +} + +$.fn.unitVal = function(n){ + var s + if (typeof n === "undefined") { + s = $(this).val() + n = stringToMeasurement( s ) + if (! n || isNaN(n)) { + n = $(this).data("px") + } + } + s = measurementToString( n ) + $(this).val( s ).data("px", n) + return n +} + +function measurementToString( n ) { + var s, ft, inch + switch (app.units) { + case 'm': + s = round(n/36 * 0.3048 * 100) / 100 + " m" + break + case 'ft': + ft = floor(n / 36) + inch = abs(round((n % 36) / 3)) + s = ft + "'" + if (inch > 0) { + s += " " + inch + '"' + } + break + case 'px': + default: + s = round(n) + " px" + break + } + return s +} +function stringToMeasurement( s ) { + var ft, inch, ft_in, type + if (! s.match(/[0-9]/)) { + return NaN + } + if (s.indexOf("'") !== -1 || s.indexOf('"') !== -1 || s.indexOf('ft') !== -1) { + ft_in = s.match(/[0-9.]+/g) + if (ft_in.length >= 2) { + ft = parseFloat( ft_in[0] ) + inch = parseFloat( ft_in[1] ) + } + else if (ft_in.length == 1) { + if (s.indexOf('"') !== -1) { + ft = 0 + inch = parseFloat( ft_in[0] ) + } + else { + ft = parseFloat( ft_in[0] ) + inch = 0 + } + } + else { + ft = inch = 0 + } + n = ft * 36 + inch * 3 + } + else if (s.indexOf("m") !== -1) { + n = parseFloat(s.match(/[0-9.]+/)) * 36 / 0.3048 + } + else if (s.indexOf("px") !== -1) { + n = parseFloat(s.match(/[0-9.]+/)) + } + else { + n = abs( stringToMeasurement( s + app.units ) ) + } + if (s.indexOf('-') !== -1) { + n *= -1 + } + return n +} diff --git a/public/assets/javascripts/rectangles/util/permissions.js b/public/assets/javascripts/rectangles/util/permissions.js index adb2498..1b5a1b5 100644 --- a/public/assets/javascripts/rectangles/util/permissions.js +++ b/public/assets/javascripts/rectangles/util/permissions.js @@ -24,9 +24,26 @@ Permissions.prototype.assign = function (key, state) { return state } +Permissions.prototype.add = function (key) { + var base = this + base[key] = true +} + +Permissions.prototype.remove = function (key) { + var base = this + base[key] = true +} + Permissions.prototype.clear = function () { var base = this base.keys.forEach(function(op){ base[op] = false }) } + +Permissions.prototype.log = function () { + var base = this + base.keys.forEach(function(op){ + console.log(op, base[op]) + }) +}
\ No newline at end of file diff --git a/public/assets/javascripts/rectangles/util/undostack.js b/public/assets/javascripts/rectangles/util/undostack.js new file mode 100644 index 0000000..b93c79e --- /dev/null +++ b/public/assets/javascripts/rectangles/util/undostack.js @@ -0,0 +1,52 @@ +(function(){ + + var UndoStack = function(){ + this.debug = true + this.stack = [] + this.types = {} + this.pointer = -1 + } + UndoStack.prototype.push = function(action){ + this.pointer++ + this.stack[this.pointer] = action + this.purge() + } + UndoStack.prototype.purge = function(){ + if (this.stack.length-1 == this.pointer) return + this.stack.length = this.pointer+1 + } + UndoStack.prototype.undo = function(){ + if (this.pointer == -1) return false + var action = this.stack[this.pointer] + this.debug && console.log("undo", action.type) + this.types[ action.type ].undo(action.undo) + this.pointer-- + return this.pointer > -1 + } + UndoStack.prototype.redo = function(){ + if (this.pointer == this.stack.length-1) return false + this.pointer++ + var action = this.stack[this.pointer] + this.debug && console.log("redo", action.type) + 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)) + } + else { + this.registerOne(actionType) + } + } + UndoStack.prototype.registerOne = function(actionType){ + this.types[ actionType.type ] = actionType + } + if ('window' in this) { + window.UndoStack = new UndoStack + } + else { + module.exports = new UndoStack + } + +})() diff --git a/public/assets/javascripts/ui/_router.js b/public/assets/javascripts/ui/_router.js index 4ff3581..d07810e 100644 --- a/public/assets/javascripts/ui/_router.js +++ b/public/assets/javascripts/ui/_router.js @@ -36,11 +36,18 @@ var SiteRouter = Router.extend({ "/project": 'projectPicker', "/project/new": 'newProject', "/project/new/:layout": 'projectNewWithLayout', - "/project/:name": 'project', + "/project/:name": 'projectViewer', + "/project/:name/edit": 'projectEditor', "/project/:name/view": 'projectViewer', "/test/wallpaper": 'testWallpaper', }, + + mobileRoutes: { + "/": 'home', + "/profile": 'profile', + "/project/:name": 'projectViewer', + }, initialize: function(){ this.signUpModal = new SignUpModal() @@ -56,6 +63,11 @@ var SiteRouter = Router.extend({ this.route() + if (is_mobile) { + $(".topLinks").hide() + $(".share").hide() + } + $("body").removeClass("loading") }, @@ -123,14 +135,6 @@ var SiteRouter = Router.extend({ this.readerView.load(name) }, -/* - editProject: function(e){ - e && e.preventDefault() - window.history.pushState(null, document.title, "/project/edit") - this.editProjectModal.load() - }, -*/ - signup: function(e){ e && e.preventDefault() window.history.pushState(null, document.title, "/signup") diff --git a/public/assets/javascripts/ui/builder/BuilderInfo.js b/public/assets/javascripts/ui/builder/BuilderInfo.js index 56f1338..2fffdba 100644 --- a/public/assets/javascripts/ui/builder/BuilderInfo.js +++ b/public/assets/javascripts/ui/builder/BuilderInfo.js @@ -96,86 +96,3 @@ var BuilderInfo = View.extend({ }, }) - -$.fn.resetUnitVal = function(){ - this.each(function(){ - var n = $(this).data("px") - $(this).unitVal(n) - }); -} - -$.fn.unitVal = function(n){ - var s - if (typeof n === "undefined") { - s = $(this).val() - n = stringToMeasurement( s ) - if (! n || isNaN(n)) { - n = $(this).data("px") - } - } - s = measurementToString( n ) - $(this).val( s ).data("px", n) - return n -} - -function measurementToString( n ) { - var s, ft, inch - switch (app.units) { - case 'm': - s = round(n/36 * 0.3048 * 100) / 100 + " m" - break - case 'ft': - ft = floor(n / 36) - inch = abs(round((n % 36) / 3)) - s = ft + "'" - if (inch > 0) { - s += " " + inch + '"' - } - break - case 'px': - default: - s = round(n) + " px" - break - } - return s -} -function stringToMeasurement( s ) { - var ft, inch, ft_in, type - if (! s.match(/[0-9]/)) { - return NaN - } - if (s.indexOf("'") !== -1 || s.indexOf('"') !== -1 || s.indexOf('ft') !== -1) { - ft_in = s.match(/[0-9.]+/g) - if (ft_in.length >= 2) { - ft = parseFloat( ft_in[0] ) - inch = parseFloat( ft_in[1] ) - } - else if (ft_in.length == 1) { - if (s.indexOf('"') !== -1) { - ft = 0 - inch = parseFloat( ft_in[0] ) - } - else { - ft = parseFloat( ft_in[0] ) - inch = 0 - } - } - else { - ft = inch = 0 - } - n = ft * 36 + inch * 3 - } - else if (s.indexOf("m") !== -1) { - n = parseFloat(s.match(/[0-9.]+/)) * 36 / 0.3048 - } - else if (s.indexOf("px") !== -1) { - n = parseFloat(s.match(/[0-9.]+/)) - } - else { - n = abs( stringToMeasurement( s + app.units ) ) - } - if (s.indexOf('-') !== -1) { - n *= -1 - } - return n -}
\ No newline at end of file diff --git a/public/assets/javascripts/ui/builder/BuilderToolbar.js b/public/assets/javascripts/ui/builder/BuilderToolbar.js index df98ab0..2eb7590 100644 --- a/public/assets/javascripts/ui/builder/BuilderToolbar.js +++ b/public/assets/javascripts/ui/builder/BuilderToolbar.js @@ -6,15 +6,22 @@ var BuilderToolbar = View.extend({ "click [data-role='toggle-map-view']": 'toggleMap', "click [data-role='toggle-layout-settings']": 'toggleSettings', "click [data-role='undo']": 'undo', - "click [data-role='create-mode']": 'create', - "click [data-role='resize-mode']": 'resize', - "click [data-role='move-mode']": 'move', +// "click [data-role='create-mode']": 'create', +// "click [data-role='resize-mode']": 'resize', +// "click [data-role='move-mode']": 'move', "click [data-role='destroy-mode']": 'destroy', }, initialize: function(opt){ this.parent = opt.parent - map.ui.permissions.toggle() + this.resetPermissions() + }, + + resetPermissions: function(){ + map.ui.permissions.clear() + map.ui.permissions.add("create") + map.ui.permissions.add("move") + map.ui.permissions.add("resize") }, toggleMap: function(){ @@ -27,7 +34,8 @@ var BuilderToolbar = View.extend({ undo: function(){ }, - + +/* create: function(e){ var state = map.ui.permissions.toggle("create") $(".inuse").removeClass("inuse") @@ -45,7 +53,8 @@ var BuilderToolbar = View.extend({ $(".inuse").removeClass("inuse") $(e.currentTarget).toggleClass("inuse", state) }, - +*/ + destroy: function(e){ var state = map.ui.permissions.toggle("destroy") $(".inuse").removeClass("inuse") diff --git a/public/assets/javascripts/ui/editor/EditorSettings.js b/public/assets/javascripts/ui/editor/EditorSettings.js index 13a6f60..d6a79fb 100644 --- a/public/assets/javascripts/ui/editor/EditorSettings.js +++ b/public/assets/javascripts/ui/editor/EditorSettings.js @@ -130,7 +130,7 @@ var EditorSettings = FormView.extend({ this.$name.val(data.name) this.action = this.updateAction - window.history.pushState(null, document.title, "/project/" + data.slug) + window.history.pushState(null, document.title, "/project/" + data.slug + "/edit") }, }) diff --git a/public/assets/javascripts/ui/editor/EditorToolbar.js b/public/assets/javascripts/ui/editor/EditorToolbar.js index a3abc5a..5e0da7e 100644 --- a/public/assets/javascripts/ui/editor/EditorToolbar.js +++ b/public/assets/javascripts/ui/editor/EditorToolbar.js @@ -30,6 +30,7 @@ var EditorToolbar = View.extend({ this.parent.mediaViewer.show() this.parent.mediaUpload.show() this.resetMode() + this.resetControls() }, resetMode: function(){ @@ -43,6 +44,7 @@ var EditorToolbar = View.extend({ }, resizeMedia: function(e, state){ + this.resetControls() if (! state && typeof e == "boolean") { state = e editor.permissions.assign("resize", state) @@ -59,6 +61,7 @@ var EditorToolbar = View.extend({ }, destroyMedia: function(e, state){ + this.resetControls() if (! state && typeof e == "boolean") { state = e editor.permissions.assign("destroy", state) @@ -73,11 +76,19 @@ var EditorToolbar = View.extend({ }, toggleWallpaper: function(){ - this.parent.wallpaperPicker.toggle() + var state = ! $("[data-role='toggle-wallpaper-panel']").hasClass("inuse") + this.resetMode() + $("[data-role='toggle-wallpaper-panel']").toggleClass("inuse", state) + this.parent.lightControl.hide() + this.parent.wallpaperPicker.toggle(state) }, toggleLightControl: function(){ - this.parent.lightControl.toggle() + var state = ! $("[data-role='toggle-light-control']").hasClass("inuse") + this.resetMode() + $("[data-role='toggle-light-control']").toggleClass("inuse", state) + this.parent.wallpaperPicker.hide() + this.parent.lightControl.toggle(state) }, editWallText: function(){ diff --git a/public/assets/javascripts/ui/editor/LightControl.js b/public/assets/javascripts/ui/editor/LightControl.js index 93d97ed..c3e80c2 100644 --- a/public/assets/javascripts/ui/editor/LightControl.js +++ b/public/assets/javascripts/ui/editor/LightControl.js @@ -6,12 +6,18 @@ var LightControl = View.extend({ "mousedown": "stopPropagation", }, - toggle: function(){ - this.$el.toggleClass("active"); + toggle: function(state){ + this.$el.toggleClass("active", state); // 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({ diff --git a/public/assets/javascripts/ui/editor/MediaEditor.js b/public/assets/javascripts/ui/editor/MediaEditor.js index cd8fb63..e3a8f2e 100644 --- a/public/assets/javascripts/ui/editor/MediaEditor.js +++ b/public/assets/javascripts/ui/editor/MediaEditor.js @@ -11,6 +11,9 @@ var MediaEditor = FormView.extend({ "change [name=autoplay]": "setAutoplay", "change [name=loop]": "setLoop", "change [name=mute]": "setMute", + "change [name=width]": 'changeWidth', + "change [name=height]": 'changeHeight', + "change [name=units]": 'changeUnits', "click [data-role=destroy-media]": "destroy", }, @@ -22,8 +25,8 @@ var MediaEditor = FormView.extend({ this.$description = this.$("[name=description]") // image fields - this.$widthDimension = this.$("[name=width]") - this.$heightDimension = this.$("[name=height]") + this.$width = this.$("[name=width]") + this.$height = this.$("[name=height]") this.$units = this.$("[name=units]") // video fields @@ -55,16 +58,14 @@ var MediaEditor = FormView.extend({ this.$name.val(media.title) this.$description.val(media.description) + this.setDimensions() + this.$units.val( "ft" ) switch (media.type) { case "image": this.$(".image").show() this.$(".video").hide() - - this.$widthDimension.val( Number(media.widthDimension) || "" ) - this.$heightDimension.val( Number(media.heightDimension) || "" ) - this.$units.val( media.units || "cm" ) - + break case "youtube": @@ -113,18 +114,39 @@ var MediaEditor = FormView.extend({ this.scenery.mute(checked) }, + setDimensions: function(){ + this.$width.unitVal( Number(this.scenery.dimensions.a * this.scenery.scale) || "" ) + this.$height.unitVal( Number(this.scenery.dimensions.b * this.scenery.scale) || "" ) + }, + changeWidth: function(e){ + e.stopPropagation() + this.scenery.set_scale( this.$width.unitVal() / this.scenery.dimensions.a ) + this.setDimensions() + }, + changeHeight: function(e){ + e.stopPropagation() + this.scenery.set_scale( this.$height.unitVal() / this.scenery.dimensions.b ) + this.setDimensions() + }, + changeUnits: function(){ + app.units = this.$units.val() + this.$('.units').resetUnitVal() + }, + bind: function(scenery){ this.scenery = scenery this.scenery.mx.bound = true }, unbind: function(){ - this.scenery.mx.bound = false - this.scenery = null + if (this.scenery && this.scenery.mx) { + this.scenery.mx.bound = false + } + this.scenery = null }, destroy: function(){ - ConfirmModal.confirm("Are you sure you want to this media?", function(){ + ConfirmModal.confirm("Are you sure you want delete to this media?", function(){ var scenery = this.scenery this.hide() Scenery.remove(scenery.id) diff --git a/public/assets/javascripts/ui/editor/WallpaperPicker.js b/public/assets/javascripts/ui/editor/WallpaperPicker.js index cb1e361..9ee441b 100644 --- a/public/assets/javascripts/ui/editor/WallpaperPicker.js +++ b/public/assets/javascripts/ui/editor/WallpaperPicker.js @@ -23,11 +23,17 @@ var WallpaperPicker = View.extend({ wm.init() }, - toggle: function(){ - this.$el.toggleClass("active"); + toggle: function(state){ + this.$el.toggleClass("active", state); // toggle the class that makes the cursor a paintbucket // $("body").removeClass("pastePaper"); }, + show: function(){ + this.toggle(true) + }, + hide: function(){ + this.toggle(false) + }, pick: function(e){ var $swatch = $(e.currentTarget) diff --git a/public/assets/javascripts/ui/lib/ModalView.js b/public/assets/javascripts/ui/lib/ModalView.js index 957a54d..937c1e9 100644 --- a/public/assets/javascripts/ui/lib/ModalView.js +++ b/public/assets/javascripts/ui/lib/ModalView.js @@ -11,9 +11,10 @@ var ModalView = View.extend({ }, show: function(){ - $(".mediaDrawer").removeClass("active"); - this.$el.addClass("active"); - $("body").addClass("noOverflow"); + $(".mediaDrawer").removeClass("active") + $(".fileUpload").removeClass("active") + this.$el.addClass("active") + $("body").addClass("noOverflow") }, hide: function(){ diff --git a/public/assets/javascripts/ui/lib/Parser.js b/public/assets/javascripts/ui/lib/Parser.js index dfff7b2..1cf0418 100644 --- a/public/assets/javascripts/ui/lib/Parser.js +++ b/public/assets/javascripts/ui/lib/Parser.js @@ -86,6 +86,10 @@ var Parser = { success: function(result){ if (result.length == 0) { return done(id, "", 640, 360) } var res = result[0] + if (res.embed_privacy != "anywhere") { + AlertModal.alert("Sorry, the author of this video has marked it private, preventing it from being embedded.", function(){}) + return + } done({ token: id, thumbnail: res.thumbnail_large, diff --git a/public/assets/javascripts/ui/lib/Router.js b/public/assets/javascripts/ui/lib/Router.js index 5877f93..0b6385c 100644 --- a/public/assets/javascripts/ui/lib/Router.js +++ b/public/assets/javascripts/ui/lib/Router.js @@ -4,7 +4,8 @@ var Router = View.extend({ this.originalPath = window.location.pathname - var pathname = window.location.pathname, + var routes = is_mobile ? this.mobileRoutes : this.routes, + pathname = window.location.pathname, path = pathname.split("/"); for (var i = 0; i < path.length; i++) { @@ -13,7 +14,7 @@ var Router = View.extend({ } } - if (pathname in this.routes) { + if (pathname in routes) { this[this.routes[pathname]](null) } @@ -21,35 +22,39 @@ var Router = View.extend({ path.pop() } - for (var route in this.routes) { + for (var route in routes) { var routePath = route.split("/") if (routePath[1] == path[1]) { if (routePath[2] && routePath[2].indexOf(":") !== -1 && path[2] && (path[3] === routePath[3]) ) { this[this.routes[route]](null, path[2]) - break; + return } else if (routePath[2] == path[2]) { if (routePath[3] && path[3]) { if (routePath[3].indexOf(":") !== -1) { this[this.routes[route]](null, path[3]) - break; + return } else if (routePath[3] == path[3]) { this[this.routes[route]](null) - break; + return } } else if (! routePath[3] && ! path[3]) { this[this.routes[route]](null) - break; + return } } else if (! routePath[2] && (! path[2].length || ! path[2])) { this[this.routes[route]](null) - break; + return } } } + + if (is_mobile) { + window.location.href = "/" + } } }) diff --git a/public/assets/javascripts/ui/site/LayoutsModal.js b/public/assets/javascripts/ui/site/LayoutsModal.js index 4948b0e..f109ec3 100644 --- a/public/assets/javascripts/ui/site/LayoutsModal.js +++ b/public/assets/javascripts/ui/site/LayoutsModal.js @@ -19,6 +19,7 @@ var LayoutsIndex = View.extend({ this.$form.hide() this.$noTemplates.show() } + this.$templates.empty() data.forEach(function(room){ var $span = $("<span>") // $span.html(JSON.stringify(room)) @@ -58,7 +59,7 @@ var ProjectsModal = ModalView.extend(LayoutsIndex.prototype).extend({ $layout.addClass("active") // actually do - window.location.pathname = "/project/" + $layout.data("slug") + window.location.pathname = "/project/" + $layout.data("slug") + "/edit" }, newProject: function(e){ @@ -103,7 +104,7 @@ var NewProjectModal = ModalView.extend(LayoutsIndex.prototype).extend({ action: "/api/layout", events: { - "click .templates span": 'toggleActive', + "click .templates span": 'choose', "submit form": 'choose', }, @@ -115,7 +116,8 @@ var NewProjectModal = ModalView.extend(LayoutsIndex.prototype).extend({ choose: function(e){ e && e.preventDefault() - var layout = this.$(".templates .active").data("slug") +// var layout = this.$(".templates .active").data("slug") + var layout = $(e.currentTarget).data("slug") if (! layout || ! layout.length) return window.location.pathname = "/project/new/" + layout } diff --git a/public/assets/javascripts/util.js b/public/assets/javascripts/util.js index 58dcc3a..b92dcf3 100644 --- a/public/assets/javascripts/util.js +++ b/public/assets/javascripts/util.js @@ -31,7 +31,7 @@ function quantize(n,a){ return round(n / a) * a } function max(a,b){ return Math.max(a,b) } function min(a,b){ return Math.min(a,b) } function abs(n){ return Math.abs(n) } -function sign(n){ return Math.abs(n)/n } +function sign(n){ return n ? Math.abs(n)/n : 0 } function pow(n,b) { return Math.pow(n,b) } function exp(n) { return Math.exp(n) } function log(n){ return Math.log(n) } diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index de9cce6..7cc41f5 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -158,13 +158,15 @@ h5 { .page .profile { color:white; } -.page table{ - width:100%; - height:400px; - border-top:1px solid; - margin:40px 0 0 0; +.page table { + width: 100%; + border-top: 1px solid; + margin: 40px 0 0 0; border-spacing: 0; - clear:both; + clear:nboth; +} +.page tr { + height: 400px; } .page table.showcase { height:70vh; @@ -230,6 +232,12 @@ iframe.embed { color:white; } +#projectList .editBtn { + position: absolute; + right: 10px; + top: 10px; +} + /* .room1 { position: relative; @@ -240,12 +248,6 @@ iframe.embed { background-image:url(https://s3.amazonaws.com/luckyplop/fd4ebe8a7a4246c8273fc999fb1ef0d6a8260b8c.png); } -.room1 .editBtn { - position: absolute; - right: 10px; - top: 10px; -} - .room1 form textarea { width: 226px; } @@ -526,20 +528,25 @@ iframe.embed { position:fixed; top:0; left:0; - cursor: -webkit-grab; - cursor: -moz-grab; z-index: 1; position: fixed; top: 50%; left: 50%; -webkit-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); - cursor:pointer; + cursor: -webkit-grab; + cursor: -moz-grab; } .mx-scene:active{ cursor: -webkit-grabbing; cursor: -moz-grabbing; } +.mx-scenery { + cursor: pointer; +} +.mx-scenery:active { + cursor: pointer; +} .mx-object3d.image, .mx-object3d.video, .mx-object3d iframe, @@ -573,6 +580,28 @@ iframe.embed { z-index: 10; } +#map { + cursor: crosshair; +} +#map.move { + cursor: move; +} +#map.ew-resize { + cursor: ew-resize; +} +#map.ns-resize { + cursor: ns-resize; +} +#map.nesw-resize { + cursor: nesw-resize; +} +#map.nwse-resize { + cursor: nwse-resize; +} +#map.destroy { + cursor: url(/assets/img/destroy-cursor.png) 12 12, auto; +} + .face { background-color: #fff; transition: 0.1s background-color ease; @@ -1268,6 +1297,13 @@ input[type="range"]::-webkit-slider-thumb { top: 0px; } +#mediaEditor .setting.number label { + width: 40px; +} +#mediaEditor .setting.number [type=text] { + width: 140px; +} + .playButton,.muteButton { color: white; background: black; diff --git a/server/index.js b/server/index.js index ad437ff..e6afdb8 100644 --- a/server/index.js +++ b/server/index.js @@ -120,8 +120,9 @@ site.route = function () { app.get('/project', middleware.ensureAuthenticated, views.modal) app.get('/project/new', middleware.ensureAuthenticated, views.modal) app.get('/project/new/:layout', middleware.ensureAuthenticated, views.editor) - app.get('/project/:slug', middleware.ensureProject, views.editor) + app.get('/project/:slug', middleware.ensureProject, views.reader) app.get('/project/:slug/view', middleware.ensureProject, views.reader) + app.get('/project/:slug/edit', middleware.ensureProject, views.editor) app.get('/api/layout', middleware.ensureAuthenticated, api.layouts.index) app.get('/api/layout/:slug', middleware.ensureAuthenticated, api.layouts.show) diff --git a/server/lib/middleware.js b/server/lib/middleware.js index aec54ad..27b9c04 100644 --- a/server/lib/middleware.js +++ b/server/lib/middleware.js @@ -36,7 +36,7 @@ var middleware = { ensureLocals: function (req, res, next) { res.locals.token = req.csrfToken(); res.locals.logged_in = req.isAuthenticated() - res.locals.user = req.user || {} + res.locals.user = req.user || { id: undefined } res.locals.config = config res.locals.profile = null res.locals.opt = {} diff --git a/server/lib/views.js b/server/lib/views.js index 81e2ced..b776582 100644 --- a/server/lib/views.js +++ b/server/lib/views.js @@ -36,6 +36,7 @@ views.editor = function (req, res) { date: moment(req.project.updated_at).format("M/DD/YYYY"), author: user.displayName, authorlink: "/profile/" + user.username, + noui: !! (req.query.noui === '1'), }) }) } @@ -45,7 +46,7 @@ views.editor = function (req, res) { } views.reader = function (req, res) { - if (! req.user && ! req.project) { + if (! req.project) { res.redirect('/') } User.findOne({ _id: req.project.user_id }, function(err, user) { @@ -60,6 +61,7 @@ views.reader = function (req, res) { date: moment(req.project.updated_at).format("M/DD/YYYY"), author: user.displayName, authorlink: "/profile/" + user.username, + noui: !! (req.query.noui === '1'), }) }) } diff --git a/test/00-setup.js b/test/00-setup.js index 78ad2c4..20f9d66 100644 --- a/test/00-setup.js +++ b/test/00-setup.js @@ -1,2 +1 @@ Error.stackTraceLimit = 5 - diff --git a/test/09-test-undo.js b/test/09-test-undo.js new file mode 100644 index 0000000..dbca90e --- /dev/null +++ b/test/09-test-undo.js @@ -0,0 +1,164 @@ +var assert = require("assert") +var UndoStack = require("../public/assets/javascripts/rectangles/util/undo.js") +UndoStack.debug = false + +describe('undo', function(){ + + var state = "zero" + + describe('#register()', function(){ + + UndoStack.register({ + type: "demo", + undo: function(myState){ + state = myState + }, + redo: function(myState){ + state = myState + }, + }) + + it('registers undoable actions', function(){ + assert( UndoStack.types.hasOwnProperty("demo") ) + }) + }) + + describe('#push()', function(){ + + it('starts empty', function(){ + assert.equal(0, UndoStack.stack.length) + assert.equal(-1, UndoStack.pointer) + }) + + it('pushes some actions', function(){ + + UndoStack.push({ + type: "demo", + undo: state, + redo: "one" + }) + state = "one" + + UndoStack.push({ + type: "demo", + undo: state, + redo: "two" + }) + state = "two" + + UndoStack.push({ + type: "demo", + undo: state, + redo: "three" + }) + state = "three" + + assert.equal(3, UndoStack.stack.length) + assert.equal(2, UndoStack.pointer) + }) + }) + + describe('#undo()', function(){ + + it('retrieves old state', function(){ + assert.equal("three", state) + UndoStack.undo() + assert.equal("two", state) + assert.equal(1, UndoStack.pointer) + }) + + it('can only undo so far', function(){ + var canUndo + + canUndo = UndoStack.undo() + assert.equal("one", state) + assert.equal(0, UndoStack.pointer) + assert.equal(true, canUndo) + + canUndo = UndoStack.undo() + assert.equal("zero", state) + assert.equal(-1, UndoStack.pointer) + assert.equal(false, canUndo) + + canUndo = UndoStack.undo() + assert.equal("zero", state) + assert.equal(-1, UndoStack.pointer) + assert.equal(false, canUndo) + }) + + }) + + describe('#redo()', function(){ + + it('reassigns new state', function(){ + UndoStack.redo() + assert.equal("one", state) + }) + + it('can only redo so far', function(){ + var canRedo + + canRedo = UndoStack.redo() + assert.equal("two", state) + assert.equal(true, canRedo) + + canRedo = UndoStack.redo() + assert.equal("three", state) + assert.equal(false, canRedo) + + canRedo = UndoStack.redo() + assert.equal("three", state) + assert.equal(false, canRedo) + }) + + it('clobbers old state if we undo then do something else', function(){ + UndoStack.undo() + assert.equal("two", state) + assert.equal(1, UndoStack.pointer) + + UndoStack.undo() + assert.equal("one", state) + assert.equal(0, UndoStack.pointer) + assert.equal(3, UndoStack.stack.length) + + UndoStack.push({ + type: "demo", + undo: state, + redo: "four" + }) + state = "four" + + assert.equal(1, UndoStack.pointer) + assert.equal(2, UndoStack.stack.length) + + UndoStack.undo() + assert.equal("one", state) + assert.equal(0, UndoStack.pointer) + assert.equal(2, UndoStack.stack.length) + + UndoStack.redo() + assert.equal("four", state) + assert.equal(1, UndoStack.pointer) + assert.equal(2, UndoStack.stack.length) + }) + }) + +}) + +// 1) push action +// 2) push action +// 3) push action +// undo 3 +// undo 2 +// undo 1 +// undo * can't undo anymore +// redo 1 +// redo 2 +// redo 3 +// redo * can't redo anymore +// undo 3 +// 4) push action (clobbers action 3) +// undo 4 +// undo 2 +// redo 2 +// redo 4 diff --git a/views/controls/builder/toolbar.ejs b/views/controls/builder/toolbar.ejs index 98aee1e..a00249c 100644 --- a/views/controls/builder/toolbar.ejs +++ b/views/controls/builder/toolbar.ejs @@ -3,6 +3,7 @@ data-role='toggle-map-view' data-info="toggle map view" class="icon-ios7-photos-outline"></span> +<!-- <span data-role='create-mode' data-info="draw" @@ -15,6 +16,7 @@ data-role='resize-mode' data-info="resize" class="icon-arrow-resize"></span> +--> <span data-role='destroy-mode' data-info="delete" diff --git a/views/controls/editor/media-editor.ejs b/views/controls/editor/media-editor.ejs index 5db1fb2..7f8f299 100644 --- a/views/controls/editor/media-editor.ejs +++ b/views/controls/editor/media-editor.ejs @@ -34,13 +34,19 @@ <input type="range" min="0" max="1" value="0" step="0.01" name="keyframe" id="video-keyframe"> </div> - <div class="image setting"> - Dimensions<br> - <input type="text" name="width" placeholder="width" class="number"> - <input type="text" name="height" placeholder="height" class="number"> - <select name="units"> - <option value="inch">inch</option> - <option value="cm">cm</option> + <div class="setting number"> + <label for="scenery-width">width</label> + <input type="text" class="units" name="width" id="scenery-width"> + </div> + <div class="setting number"> + <label for="scenery-height">height</label> + <input type="text" class="units" name="height" id="scenery-height"> + </div> + <div class="setting number"> + <select id="builder-units" name="units"> + <option value="px">pixels</option> + <option value="ft">foot</option> + <option value="m">meter</option> </select> </div> diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs index acb3bff..d0454d6 100644 --- a/views/partials/scripts.ejs +++ b/views/partials/scripts.ejs @@ -12,6 +12,7 @@ <script type="text/javascript" src="/assets/javascripts/mx/mx.js"></script> <script type="text/javascript" src="/assets/javascripts/mx/extensions/mx.scene.js"></script> <script type="text/javascript" src="/assets/javascripts/mx/extensions/mx.movements.js"></script> +<script type="text/javascript" src="/assets/javascripts/mx/extensions/mx.movementsMobile.js"></script> <script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.image.js"></script> <script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.video.js"></script> <script type="text/javascript" src="/assets/javascripts/mx/primitives/mx.youtube.js"></script> @@ -22,11 +23,13 @@ <script type="text/javascript" src="/assets/javascripts/rectangles/util/colors.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/util/debug.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/util/permissions.js"></script> +<script type="text/javascript" src="/assets/javascripts/rectangles/util/measurement.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/util/sort.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/util/uid.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/util/wheel.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/util/mouse.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/util/keys.js"></script> +<script type="text/javascript" src="/assets/javascripts/rectangles/util/undostack.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/models/vec2.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/models/vec3.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/models/mat4.js"></script> @@ -45,6 +48,7 @@ <script type="text/javascript" src="/assets/javascripts/rectangles/engine/scenery/_scenery.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/engine/scenery/move.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/engine/scenery/resize.js"></script> +<script type="text/javascript" src="/assets/javascripts/rectangles/engine/scenery/undo.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/engine/scenery/types/_object.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/engine/scenery/types/image.js"></script> <script type="text/javascript" src="/assets/javascripts/rectangles/engine/scenery/types/video.js"></script> diff --git a/views/projects/list-projects.ejs b/views/projects/list-projects.ejs index d2c0447..c78bf9f 100644 --- a/views/projects/list-projects.ejs +++ b/views/projects/list-projects.ejs @@ -14,10 +14,13 @@ <td class="border room1"> [[ } ]] <iframe src="/project/[[- project.slug ]]/view?noui=1&mute=1" class="embed"></iframe> - [[ if (profile && profile._id == project.user_id) { ]] - <div class="editBtn">edit</div> + [[ if (String(user._id) == String(project.user_id)) { ]] + <a href="/project/[[- project.slug ]]/edit"><div class="editBtn">edit</div></a> [[ } ]] - <a href="/project/[[- project.slug ]]" class="roomName">[[- project.name ]]<br>[[- project.date ]]</a> + <a href="/project/[[- project.slug ]]" class="roomName"> + [[- project.name ]]<br> + [[- project.date ]] + </a> </td> [[ }) ]] diff --git a/views/reader.ejs b/views/reader.ejs index 44fb2dd..7c31766 100644 --- a/views/reader.ejs +++ b/views/reader.ejs @@ -8,6 +8,7 @@ <div id="scene"></div> + [[ if (! noui) { ]] <div class="rapper"> [[ include partials/header ]] @@ -16,6 +17,7 @@ [[ include controls/reader/media-player ]] </div> </div> + [[ } ]] [[ include partials/confirm-modal ]] [[ include projects/layouts-modal ]] |
