diff options
Diffstat (limited to 'public/assets/javascripts/ui')
26 files changed, 915 insertions, 91 deletions
diff --git a/public/assets/javascripts/ui/_router.js b/public/assets/javascripts/ui/_router.js index 6d41d5b..d070d55 100644 --- a/public/assets/javascripts/ui/_router.js +++ b/public/assets/javascripts/ui/_router.js @@ -5,6 +5,7 @@ var SiteRouter = Router.extend({ events: { "click [data-role='show-signup-modal']": 'signup', "click [data-role='show-signin-modal']": 'signin', + "click [data-role='forgot-password']": 'passwordForgot', "click [data-role='new-project-modal']": 'newProject', "click [data-role='edit-project-modal']": 'editProject', "click [data-role='edit-profile-modal']": 'editProfile', @@ -18,6 +19,11 @@ var SiteRouter = Router.extend({ routes: { "/login": 'signin', "/signup": 'signup', + + "/auth/usernameTaken": 'usernameTaken', + "/auth/password": 'passwordReset', + "/auth/forgotPassword": 'passwordForgot', + "/profile": 'profile', "/profile/edit": 'editProfile', "/about/:name/edit": 'editDocument', @@ -29,7 +35,10 @@ var SiteRouter = Router.extend({ "/project": 'projectPicker', "/project/new": 'newProject', "/project/new/:layout": 'projectNewWithLayout', - "/project/:name": 'projectEditor', + "/project/:name": 'project', + "/project/:name/view": 'projectViewer', + + "/test/wallpaper": 'testWallpaper', }, initialize: function(){ @@ -40,7 +49,9 @@ var SiteRouter = Router.extend({ this.newProjectModal = new NewProjectModal() this.editProjectModal = new EditProjectModal() this.editProfileModal = new EditProfileModal() + this.passwordForgotModal = new PasswordForgot() this.documentModal = new DocumentModal() + this.profileView = new ProfileView() this.route() @@ -52,7 +63,7 @@ var SiteRouter = Router.extend({ app.mode.builder = true app.launch() - this.builderView = new BuilderView() + this.builderView = app.controller = new BuilderView() this.builderView.load(name) }, @@ -83,25 +94,35 @@ var SiteRouter = Router.extend({ layout = slugify(layout) window.history.pushState(null, document.title, "/project/new/" + layout) - this.editorView = new EditorView() + this.editorView = app.controller = new EditorView() this.editorView.loadLayout(layout) }, - projectEditor: function(e, name){ - app.mode.editor = true - app.launch() - + project: function(e, name){ if ($(".aboutRoom").length) { - this.readerView = new ReaderView() - this.readerView.load(name) + this.projectViewer(e, name) } else { - this.editorView = new EditorView() - this.editorView.load(name) + this.projectEditor(e, name) } }, + projectEditor: function(e, name){ + app.mode.editor = true + app.launch() + + this.editorView = app.controller = new EditorView() + this.editorView.load(name) + }, + projectViewer: function(e, name){ + app.mode.editor = true + app.launch() + + this.readerView = app.controller = new ReaderView() + this.readerView.load(name) + }, + /* editProject: function(e){ e && e.preventDefault() @@ -122,14 +143,23 @@ var SiteRouter = Router.extend({ this.signInModal.load() }, - profile: function(e){ - var classes = ['one', 'two', 'three', 'four', - 'five', 'six', 'seven', 'eight', - 'nine', 'ten', 'eleven', 'twelve', - 'thirteen']; - $(".bio").addClass(choice(classes)); + usernameTaken: function(e){ + this.usernameTakenModal = new UsernameTaken () + this.usernameTakenModal.load() + }, + passwordForgot: function(e){ + e && e.preventDefault() + window.history.pushState(null, document.title, "/auth/forgotPassword") + this.passwordForgotModal.load() + }, + passwordReset: function(e){ + this.passwordResetModal = new PasswordReset () + this.passwordResetModal.load() }, + profile: function(e){ + this.profileView.load() + }, editProfile: function(e){ e && e.preventDefault() window.history.pushState(null, document.title, "/profile/edit") @@ -160,16 +190,41 @@ var SiteRouter = Router.extend({ var name = e ? $(e.currentTarget).data("name") : name - confirmModal.confirm("Are you sure you want to delete " + name + "?", $.proxy(function(){ - this.documentModal.destroy(name, $.proxy(function(){ - AlertModal.alert("Document deleted!", $.proxy(function(){ + ConfirmModal.confirm("Are you sure you want to delete " + name + "?", function(){ + this.documentModal.destroy(name, function(){ + AlertModal.alert("Document deleted!", function(){ window.location.href = "/about" - }, this)) - }, this)) - }, this)) + }.bind(this)) + }.bind(this)) + }.bind(this)) // this.documentModal.destroy(name) }, + + testWallpaper: function(e){ + var content = document.getElementById("content") + content.style.width = "680px" + content.style.margin = "0 auto" + var wm = new WallpaperManager() + app.on('wallpaper-ready', function(){ + var black = [0,0,0,0] + var white = [255,255,255,1.0] + var swatches = wm.buildSwatches(black, white, 4) + document.body.style.backgroundColor = "#eee" + swatches.forEach(function(swatch){ + swatch.style.margin = "4px" + swatch.style.border = "1px solid lime" + swatch.style.backgroundColor = "#888" + content.appendChild(swatch) + swatch.onclick = function(){ + dataUrl = swatch.toDataURL() + document.body.style.backgroundImage = "url(" + dataUrl + ")" + } + }) + }) + wm.init() + }, + }) diff --git a/public/assets/javascripts/ui/builder/BuilderInfo.js b/public/assets/javascripts/ui/builder/BuilderInfo.js new file mode 100644 index 0000000..9bbd385 --- /dev/null +++ b/public/assets/javascripts/ui/builder/BuilderInfo.js @@ -0,0 +1,160 @@ + +var BuilderInfo = View.extend({ + el: "#builderInfo", + + events: { + "keydown": 'stopPropagation', + "change [name=x]": 'changePosition', + "change [name=y]": 'changePosition', + "change [name=width]": 'changeDimensions', + "change [name=depth]": 'changeDimensions', + "change [name=height]": 'changeDimensions', + "change [name=units]": 'changeUnits', + "change [name=resolution]": 'changeResolution', + "change [name=viewHeight]": 'changeViewHeight', + }, + + initialize: function(opt){ + this.parent = opt.parent + this.$x = this.$("[name=x]") + this.$y = this.$("[name=y]") + this.$width = this.$("[name=width]") + this.$depth = this.$("[name=depth]") + this.$height = this.$("[name=height]") + this.$units = this.$("[name=units]") + this.$viewHeight = this.$("[name=viewHeight]") + this.$unitName = this.$(".unitName") + app.on("builder-pick-room", this.pick.bind(this)) + app.on("builder-destroy-room", this.destroy.bind(this)) + }, + + load: function(data){ + this.$viewHeight.unitVal( data.viewHeight || app.defaults.viewHeight ) + this.$units.val( "ft" ) + this.$unitName.html( "ft" ) + this.$resolution.val( app.defaults.footResolution ) + }, + + toggle: function(state){ + this.$el.toggleClass("active", state) + }, + + show: function(){ + this.toggle(true) + }, + + hide: function(){ + this.toggle(false) + }, + + room: null, + + pick: function(room){ + this.room = room + this.$width.unitVal( room.rect.x.length() ) + this.$depth.unitVal( room.rect.y.length() ) + this.$height.unitVal( room.height ) + this.$x.unitVal( room.rect.x.a ) + this.$y.unitVal( room.rect.y.a ) + + console.log(room) + }, + + destroy: function(room){ + this.room = null + this.hide() + }, + + changeDimensions: function(){ +// this.$width.unitVal( room.rect.x.length() ) +// this.$depth.unitVal( room.rect.y.length() ) +// this.$height.unitVal( room.height ) + }, + + changeUnits: function(){ + app.units = this.$units.val() + this.$('.units').resetUnitVal() + }, + + changeViewHeight: function(){ + window.viewHeight = this.$viewHeight.unitVal( ) + }, + +}) + +$.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 ) + } + 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.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 (type === "m") { + 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/BuilderSettings.js b/public/assets/javascripts/ui/builder/BuilderSettings.js index 9b2f753..c551f95 100644 --- a/public/assets/javascripts/ui/builder/BuilderSettings.js +++ b/public/assets/javascripts/ui/builder/BuilderSettings.js @@ -2,11 +2,12 @@ var BuilderSettings = FormView.extend({ el: "#builderSettings", - createAction: "/api/layouts/new", - updateAction: "/api/layouts/edit", - destroyAction: "/api/layouts/destroy", + createAction: "/api/layout/new", + updateAction: "/api/layout/edit", + destroyAction: "/api/layout/destroy", events: { + "keydown": 'stopPropagation', "keydown [name=name]": 'enterSubmit', "click [data-role='save-layout']": 'save', "click [data-role='clone-layout']": 'clone', @@ -57,7 +58,7 @@ var BuilderSettings = FormView.extend({ destroy: function(){ var msg = "Are you sure you want to delete the layout " + sanitize(this.$name.val()) + "?" - ConfirmModal.confirm(msg, $.proxy(function(){ + ConfirmModal.confirm(msg, function(){ $.ajax({ url: this.destroyAction, type: "delete", @@ -66,11 +67,11 @@ var BuilderSettings = FormView.extend({ window.location.href = "/layout" } }) - }, this)) + }.bind(this)) }, - toggle: function(){ - this.$el.toggleClass("active") + toggle: function(state){ + this.$el.toggleClass("active", state) }, enterSubmit: function (e) { diff --git a/public/assets/javascripts/ui/builder/BuilderView.js b/public/assets/javascripts/ui/builder/BuilderView.js index 33aface..555cd58 100644 --- a/public/assets/javascripts/ui/builder/BuilderView.js +++ b/public/assets/javascripts/ui/builder/BuilderView.js @@ -2,12 +2,13 @@ var BuilderView = View.extend({ el: "#builderView", - action: "/api/layouts/", + action: "/api/layout/", events: { }, initialize: function(){ + this.info = new BuilderInfo ({ parent: this }) this.toolbar = new BuilderToolbar ({ parent: this }) this.settings = new BuilderSettings ({ parent: this }) }, @@ -20,11 +21,15 @@ var BuilderView = View.extend({ name = sanitize(name) - $.get(this.action + name, $.proxy(this.ready, this)) + $.get(this.action + name, this.ready.bind(this)) }, ready: function(data){ this.settings.load(data) + this.info.load(data) }, + + hideExtras: function(){ + } }) diff --git a/public/assets/javascripts/ui/editor/EditorSettings.js b/public/assets/javascripts/ui/editor/EditorSettings.js index b9f4560..13a6f60 100644 --- a/public/assets/javascripts/ui/editor/EditorSettings.js +++ b/public/assets/javascripts/ui/editor/EditorSettings.js @@ -2,11 +2,12 @@ var EditorSettings = FormView.extend({ el: "#editorSettings", - createAction: "/api/projects/new", - updateAction: "/api/projects/edit", - destroyAction: "/api/projects/destroy", + createAction: "/api/project/new", + updateAction: "/api/project/edit", + destroyAction: "/api/project/destroy", events: { + "keydown": 'stopPropagation', "keydown [name=name]": 'enterSubmit', "click [data-role='save-project']": 'save', "click [data-role='clone-project']": 'clone', @@ -32,7 +33,7 @@ var EditorSettings = FormView.extend({ data.startPosition && scene.camera.move(data.startPosition) if (! data.isNew) { - console.log(data) + // console.log(data) this.$id.val( data._id ) this.$name.val( data.name ) @@ -65,7 +66,7 @@ var EditorSettings = FormView.extend({ destroy: function(){ var msg = "Are you sure you want to delete the project " + sanitize(this.$name.val()) + "?" - ConfirmModal.confirm(msg, $.proxy(function(){ + ConfirmModal.confirm(msg, function(){ $.ajax({ url: this.destroyAction, type: "delete", @@ -74,7 +75,7 @@ var EditorSettings = FormView.extend({ window.location.href = "/project" } }) - }, this)) + }.bind(this)) }, toggle: function(){ diff --git a/public/assets/javascripts/ui/editor/EditorToolbar.js b/public/assets/javascripts/ui/editor/EditorToolbar.js index 210ef6c..a3abc5a 100644 --- a/public/assets/javascripts/ui/editor/EditorToolbar.js +++ b/public/assets/javascripts/ui/editor/EditorToolbar.js @@ -87,6 +87,7 @@ var EditorToolbar = View.extend({ var editor = new function(){ this.permissions = new Permissions({ + 'pick': true, 'move': true, 'resize': false, 'destroy': false, diff --git a/public/assets/javascripts/ui/editor/EditorView.js b/public/assets/javascripts/ui/editor/EditorView.js index b87ac83..4067c4d 100644 --- a/public/assets/javascripts/ui/editor/EditorView.js +++ b/public/assets/javascripts/ui/editor/EditorView.js @@ -2,8 +2,8 @@ var EditorView = View.extend({ el: "#editorView", - projectAction: "/api/projects/", - layoutAction: "/api/layouts/", + projectAction: "/api/project/", + layoutAction: "/api/layout/", events: { }, @@ -13,18 +13,19 @@ var EditorView = View.extend({ this.settings = new EditorSettings ({ parent: this }) this.mediaViewer = new MediaViewer ({ parent: this }) this.mediaUpload = new MediaUpload ({ parent: this }) + this.mediaEditor = new MediaEditor ({ parent: this }) this.wallpaperPicker = new WallpaperPicker ({ parent: this }) this.lightControl = new LightControl ({ parent: this }) }, load: function(name){ name = sanitize(name) - $.get(this.projectAction + name, $.proxy(this.ready, this)) + $.get(this.projectAction + name, this.ready.bind(this)) }, loadLayout: function(layout){ layout = sanitize(layout) - $.get(this.layoutAction + layout, $.proxy(this.readyLayout, this)) + $.get(this.layoutAction + layout, this.readyLayout.bind(this)) }, ready: function(data){ @@ -36,7 +37,14 @@ var EditorView = View.extend({ readyLayout: function(data){ data.isNew = true this.ready(data) + }, + + pick: function(scenery){ + this.mediaEditor.pick(scenery) + }, + + hideExtras: function(){ + this.mediaEditor.hide() } }) - diff --git a/public/assets/javascripts/ui/editor/LightControl.js b/public/assets/javascripts/ui/editor/LightControl.js index 20c3577..93d97ed 100644 --- a/public/assets/javascripts/ui/editor/LightControl.js +++ b/public/assets/javascripts/ui/editor/LightControl.js @@ -3,6 +3,7 @@ var LightControl = View.extend({ el: ".lightcontrol", events: { + "mousedown": "stopPropagation", }, toggle: function(){ diff --git a/public/assets/javascripts/ui/editor/MediaEditor.js b/public/assets/javascripts/ui/editor/MediaEditor.js new file mode 100644 index 0000000..a075e6b --- /dev/null +++ b/public/assets/javascripts/ui/editor/MediaEditor.js @@ -0,0 +1,134 @@ + +var MediaEditor = FormView.extend({ + el: "#mediaEditor", + + events: { + "keydown": 'stopPropagation', + "click [data-role=play-media]": "togglePaused", + "mousedown [name=keyframe]": "stopPropagation", + "mousedown": "stopPropagation", + "change [name=keyframe]": "seek", + "change [name=autoplay]": "setAutoplay", + "change [name=loop]": "setLoop", + "change [name=mute]": "setMute", + "click [data-role=destroy-media]": "destroy", + }, + + initialize: function(opt){ + this.parent = opt.parent + this.__super__.initialize.call(this) + + this.$name = this.$("[name=name]") + this.$description = this.$("[name=description]") + + // image fields + this.$widthDimension = this.$("[name=width]") + this.$heightDimension = this.$("[name=height]") + this.$units = this.$("[name=units]") + + // video fields + this.$playButton = this.$("[data-role=play-media]") + this.$autoplay = this.$("[name=autoplay]") + this.$loop = this.$("[name=loop]") + this.$mute = this.$("[name=mute]") + this.$keyframe = this.$("[name=keyframe]") + }, + + toggle: function(state) { + this.$el.toggleClass("active", state); + }, + + togglePaused: function(state){ + var state = this.scenery.toggle(state) + this.$playButton.toggleClass("paused", ! state) + }, + + pick: function(scenery) { + if (this.scenery) { + this.unbind() + } + + this.bind(scenery) + this.$el.addClass("active") + + var media = scenery.media + + this.$name.val(media.title) + this.$description.val(media.description) + + 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": + case "vimeo": + case "video": + this.$(".video").show() + this.$(".image").hide() + + this.$playButton.toggleClass("paused", ! this.scenery.paused()) + this.$autoplay.prop('checked', !! media.autoplay) + this.$loop.prop('checked', !! media.loop) + this.$mute.prop('checked', !! media.mute) + this.$keyframe.val( Number(media.keyframe || 0) ) + + break + } + }, + + hide: function(scenery){ + if (this.scenery) { + this.unbind() + } + this.toggle(false) + }, + + seek: function(){ + var n = parseFloat( this.$keyframe.val() ) + this.scenery.seek(n) + + this.scenery.media.keyframe = n + }, + setAutoplay: function(){ + var checked = this.$autoplay.prop('checked') + this.scenery.media.autoplay = checked + if (checked && this.scenery.paused()) { + this.togglePaused() + } + }, + setLoop: function(){ + var checked = this.$loop.prop('checked') + this.scenery.media.loop = checked + }, + setMute: function(){ + var checked = this.$mute.prop('checked') + this.scenery.media.mute = checked + this.scenery.mute(checked) + }, + + bind: function(scenery){ + this.scenery = scenery + this.scenery.mx.bound = true + }, + + unbind: function(){ + this.scenery.mx.bound = false + this.scenery = null + }, + + destroy: function(){ + ConfirmModal.confirm("Are you sure you want to this media?", function(){ + var scenery = this.scenery + this.hide() + Scenery.remove(scenery.id) + }.bind(this)) + }, + +}) diff --git a/public/assets/javascripts/ui/editor/MediaUpload.js b/public/assets/javascripts/ui/editor/MediaUpload.js index b7fea07..86bf767 100644 --- a/public/assets/javascripts/ui/editor/MediaUpload.js +++ b/public/assets/javascripts/ui/editor/MediaUpload.js @@ -39,7 +39,7 @@ var MediaUpload = View.extend({ var url = this.$url.val() this.$url.val("") - Parser.parse(url, $.proxy(function(media){ + Parser.parse(url, function(media){ if (! media) { alert("Not a valid image/video link") return @@ -53,8 +53,8 @@ var MediaUpload = View.extend({ url: this.createAction, data: media, }) - request.done($.proxy(this.add, this)) - }, this)) + request.done(this.add.bind(this)) + }.bind(this)) }, handleFileSelect: function(e) { @@ -109,7 +109,7 @@ var MediaUpload = View.extend({ processData: false, contentType: false, }) - request.done($.proxy(this.add, this)) + request.done(this.add.bind(this)) }, add: function(media){ diff --git a/public/assets/javascripts/ui/editor/MediaViewer.js b/public/assets/javascripts/ui/editor/MediaViewer.js index 2535f1b..264ed7c 100644 --- a/public/assets/javascripts/ui/editor/MediaViewer.js +++ b/public/assets/javascripts/ui/editor/MediaViewer.js @@ -40,12 +40,12 @@ var MediaViewer = ModalView.extend({ }, load: function(){ - $.get("/api/media/user", $.proxy(this.populate, this)) + $.get("/api/media/user", this.populate.bind(this)) }, populate: function(data){ this.loaded = true - data && data.forEach($.proxy(this.add, this)) + data && data.forEach(this.add.bind(this)) this.__super__.show.call(this) }, @@ -62,6 +62,15 @@ var MediaViewer = ModalView.extend({ case 'vimeo': image.src = media.thumbnail break + + case 'video': + image = document.createElement('video') + image.addEventListener("loadedmetadata", function(){ + image.currentTime = image.duration * 1/3 + }) + image.src = media.url + image.load() + break } $span.data("media", media) @@ -124,7 +133,16 @@ var MediaViewer = ModalView.extend({ var $floatingImg = $('.floatingImg'); Scenery.nextMedia = media - $floatingImg.attr('src', image.attr('src')); + + switch (media.type) { + case "video": + $floatingImg.attr('src', 'http://www.rawrcast.com/wp-content/uploads/2010/02/BluePlayButton.png') + break + + default: + $floatingImg.attr('src', image.attr('src')) + break + } var height = $floatingImg.height() var width = $floatingImg.width() diff --git a/public/assets/javascripts/ui/editor/WallpaperPicker.js b/public/assets/javascripts/ui/editor/WallpaperPicker.js index ffbd935..cb1e361 100644 --- a/public/assets/javascripts/ui/editor/WallpaperPicker.js +++ b/public/assets/javascripts/ui/editor/WallpaperPicker.js @@ -3,7 +3,24 @@ var WallpaperPicker = View.extend({ el: ".wallpaper", events: { - "click .paper1": 'pick', + "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() }, toggle: function(){ @@ -13,8 +30,116 @@ var WallpaperPicker = View.extend({ }, pick: function(e){ - $("body").toggleClass("pastePaper"); - $(e.currentTarget).toggleClass("active"); + var $swatch = $(e.currentTarget) + var $floatingSwatch = $(".floatingSwatch") + + $floatingSwatch.css('background-image', $swatch.css('background-image')) + + Scenery.nextWallpaper = $swatch.css('background-image') + + setTimeout(function(){ + function _followCursor(e) { + $floatingSwatch.css({ + top: (e.pageY + 10) + 'px', + left: (e.pageX + 10) + 'px' + }); + } + $(window).on('mousemove', _followCursor); + $(window).one('click', function () { + $(window).off('mousemove', _followCursor); + $floatingSwatch.hide(); + }); + $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 0106ce0..219952d 100644 --- a/public/assets/javascripts/ui/lib/FormView.js +++ b/public/assets/javascripts/ui/lib/FormView.js @@ -26,9 +26,9 @@ var FormView = View.extend({ this.$errorList.append('<div>' + errors[i] + '</div>'); } this.$errors.css("opacity", 1.0); - setTimeout($.proxy(function(){ + setTimeout(function(){ this.$errors.show().css("opacity", 1.0); - }, this), 200) + }.bind(this), 200) } }, diff --git a/public/assets/javascripts/ui/lib/Parser.js b/public/assets/javascripts/ui/lib/Parser.js index 705ff04..dfff7b2 100644 --- a/public/assets/javascripts/ui/lib/Parser.js +++ b/public/assets/javascripts/ui/lib/Parser.js @@ -2,12 +2,18 @@ var Parser = { integrations: [{ type: 'image', regex: /\.(jpeg|jpg|gif|png|svg)(\?.*)?$/i, - async: false, fetch: function(url, done) { var img = new Image () img.onload = function(){ - done("", "", img.naturalWidth, img.naturalHeight, "") + var width = img.naturalWidth, height = img.naturalHeight img = null + done({ + token: "", + thumbnail: "", + title: "", + width: width, + height: height, + }) } img.src = url if (img.complete) { @@ -18,9 +24,30 @@ var Parser = { return '<img src="' + media.url + '" onerror="imgError(this);">'; } }, { + type: 'video', + regex: /\.(mp4|webm)(\?.*)?$/i, + fetch: function(url, done) { + var video = document.createElement("video") + video.addEventListener("loadedmetadata", function(){ + var width = video.videoWidth, height = video.videoHeight + video = null + done({ + token: "", + thumbnail: "", + title: "", + width: width, + height: height, + }) + }) + video.src = url + video.load() + }, + tag: function (media) { + return '<video src="' + media.url + '" onerror="imgError(this);">'; + } + }, { type: 'youtube', regex: /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/ ]{11})/i, - async: false, fetch: function(url, done) { var id = (url.match(/v=([-_a-zA-Z0-9]{11})/i) || url.match(/youtu.be\/([-_a-zA-Z0-9]{11})/i) || url.match(/embed\/([-_a-zA-Z0-9]{11})/i))[1].split('&')[0]; var thumb = "http://i.ytimg.com/vi/" + id + "/hqdefault.jpg" @@ -34,8 +61,14 @@ var Parser = { part: "id,contentDetails,snippet,status", }, success: function(result){ - var res = res.items[0] - done(id, thumb, 640, 360, res.snippet.title); + var res = result.items[0] + done({ + token: id, + thumbnail: thumb, + title: res.snippet.title, + width: 640, + height: 360, + }) } }) }, @@ -45,7 +78,6 @@ var Parser = { }, { type: 'vimeo', regex: /vimeo.com\/\d+$/i, - async: true, fetch: function(url, done) { var id = url.match(/\d+$/i)[0]; $.ajax({ @@ -54,7 +86,13 @@ var Parser = { success: function(result){ if (result.length == 0) { return done(id, "", 640, 360) } var res = result[0] - done(id, res.thumbnail_large, res.width, res.height, res.title) + done({ + token: id, + thumbnail: res.thumbnail_large, + title: res.title, + width: res.width, + height: res.height, + }) } }) }, @@ -66,7 +104,6 @@ var Parser = { { type: 'soundcloud', regex: /soundcloud.com\/[-a-zA-Z0-9]+\/[-a-zA-Z0-9]+\/?$/i, - async: true, fetch: function (url, done) { $.ajax({ type: 'GET', @@ -75,7 +112,13 @@ var Parser = { + '&client_id=' + '0673fbe6fc794a7750f680747e863b10', success: function(result) { - done(result.id, ""); + done({ + token: result.id, + thumbnail: "", + title: "", + width: 100, + height: 100, + }) } }); }, @@ -87,9 +130,14 @@ var Parser = { }, { type: 'link', regex: /^http.+/i, - async: false, fetch: function(url, done) { - done("", "") + done({ + token: "", + thumbnail: "", + title: "", + width: 100, + height: 100, + }) }, tag: function (media) { return '<a href="' + media.url + '" target="_blank">' + media.url + '</a>' @@ -101,16 +149,10 @@ var Parser = { parse: function (url, cb) { var matched = Parser.integrations.some(function(integration){ if (integration.regex.test(url)) { - integration.fetch(url, function(token, thumbnail, width, height, title){ - cb({ - token: token, - thumbnail: thumbnail, - type: integration.type, - title: title, - width: width, - height: height, - url: url, - }) + integration.fetch(url, function(res){ + res.url = url + res.type = integration.type + cb(res) }) return true } diff --git a/public/assets/javascripts/ui/lib/Router.js b/public/assets/javascripts/ui/lib/Router.js index 214603c..d27654a 100644 --- a/public/assets/javascripts/ui/lib/Router.js +++ b/public/assets/javascripts/ui/lib/Router.js @@ -10,6 +10,10 @@ var Router = View.extend({ path[i] = null } } + + if (path[path.length-1] == null) { + path.pop() + } for (var route in this.routes) { var routePath = route.split("/") diff --git a/public/assets/javascripts/ui/lib/View.js b/public/assets/javascripts/ui/lib/View.js index 5fc6736..999a0e5 100644 --- a/public/assets/javascripts/ui/lib/View.js +++ b/public/assets/javascripts/ui/lib/View.js @@ -84,7 +84,11 @@ var View = (function($, _){ preventDefault: function(e){ e && e.preventDefault() - } + }, + + stopPropagation: function(e){ + e && e.stopPropagation() + }, }); diff --git a/public/assets/javascripts/ui/reader/MediaPlayer.js b/public/assets/javascripts/ui/reader/MediaPlayer.js new file mode 100644 index 0000000..df2d075 --- /dev/null +++ b/public/assets/javascripts/ui/reader/MediaPlayer.js @@ -0,0 +1,100 @@ + +var MediaPlayer = FormView.extend({ + el: "#mediaPlayer", + + events: { + "click [data-role=play-media]": "togglePaused", + "click [data-role=mute-media]": "toggleMuted", + "mousedown": "stopPropagation", + }, + + initialize: function(opt){ + this.parent = opt.parent + this.__super__.initialize.call(this) + + this.$name = this.$(".name") + this.$description = this.$(".description") + + // image fields + this.$dimensions = this.$(".dimensions") + + // video fields + this.$playButton = this.$("[data-role=play-media]") + this.$muteButton = this.$("[data-role=mute-media]") + }, + + toggle: function(state) { + this.$el.toggleClass("active", state); + }, + + togglePaused: function(state){ + var state = this.scenery.toggle(state) + this.$playButton.toggleClass("paused", ! state) + }, + + toggleMuted: function(state){ + var state = this.scenery.toggleMuted(state) + this.$muteButton.toggleClass("muted", state) + }, + + pick: function(scenery) { + var media = scenery.media + + if (this.scenery) { + this.unbind() + } + if (media.type == "image") { + if ((! media.title || ! media.title.length) && (! media.description || ! media.description.length)) { + this.hide() + return + } + } + + this.bind(scenery) + this.$el.addClass("active") + + this.$name.html(media.title) + this.$description.html(media.description) + + switch (media.type) { + case "image": + this.$(".image").show() + this.$(".video").hide() + +// this.$widthDimension.html( Number(media.widthDimension) || "" ) +// this.$heightDimension.html( Number(media.heightDimension) || "" ) +// this.$units.html( media.units || "cm" ) + + break + + case "youtube": + case "vimeo": + case "video": + this.$(".video").show() + this.$(".image").hide() + + this.$playButton.toggleClass("paused", ! this.scenery.paused()) + this.$muteButton.toggleClass("muted", this.scenery.muted()) + + break + } + }, + + hide: function(scenery){ + if (this.scenery) { + this.unbind() + } + this.toggle(false) + }, + + bind: function(scenery){ + this.scenery = scenery + this.scenery.mx.bound = true + }, + + unbind: function(){ + this.scenery.mx.bound = false + this.scenery = null + }, + +}) diff --git a/public/assets/javascripts/ui/reader/ReaderView.js b/public/assets/javascripts/ui/reader/ReaderView.js index 001d097..9d38daa 100644 --- a/public/assets/javascripts/ui/reader/ReaderView.js +++ b/public/assets/javascripts/ui/reader/ReaderView.js @@ -2,17 +2,24 @@ var ReaderView = View.extend({ el: "#readerView", - projectAction: "/api/projects/", + projectAction: "/api/project/", events: { }, initialize: function(){ + this.mediaPlayer = new MediaPlayer ({ parent: this }) }, load: function(name){ + if (window.location.search.indexOf("noui") !== -1) { + $(".logo,.topLinks,#editorView").hide() + } + if (window.location.search.indexOf("mute") !== -1) { + app.muted = true + } name = sanitize(name) - $.get(this.projectAction + name, $.proxy(this.ready, this)) + $.get(this.projectAction + name, this.ready.bind(this)) }, ready: function(data){ @@ -23,6 +30,45 @@ var ReaderView = View.extend({ data.startPosition && scene.camera.move(data.startPosition) editor.permissions.clear() + + this.listen() + }, + + listen: function(){ + var base = this + + $(window).on('message', function(event){ + if (event.originalEvent.origin !== window.location.origin) { + return + } + var message = event.originalEvent.data + switch (message) { + case "spin-on": + base.spinning = true + break + case "spin-off": + base.spinning = false + break + } + }) + + requestAnimationFrame(this.spin.bind(this)) + }, + + spinning: false, + spin: function(){ + requestAnimationFrame(this.spin.bind(this)) + if (this.spinning) { + scene.camera.rotationY -= 1/180 + } + }, + + pick: function(scenery){ + this.mediaPlayer.pick(scenery) + }, + + hideExtras: function(){ + this.mediaPlayer.hide() } }) diff --git a/public/assets/javascripts/ui/site/DocumentModal.js b/public/assets/javascripts/ui/site/DocumentModal.js index 6f16169..067977d 100644 --- a/public/assets/javascripts/ui/site/DocumentModal.js +++ b/public/assets/javascripts/ui/site/DocumentModal.js @@ -20,7 +20,7 @@ var DocumentModal = ModalFormView.extend({ this.action = this.updateAction - $.get("/api/docs", { name: name }, $.proxy(function(data){ + $.get("/api/docs", { name: name }, function(data){ if (data.isNew) { this.action = this.createAction } @@ -31,7 +31,7 @@ var DocumentModal = ModalFormView.extend({ this.$("[name='new_name']").val(name) this.show() - }, this)) + }.bind(this)) }, success: function(res){ diff --git a/public/assets/javascripts/ui/site/EditProfileModal.js b/public/assets/javascripts/ui/site/EditProfileModal.js index 6b89ad8..b023923 100644 --- a/public/assets/javascripts/ui/site/EditProfileModal.js +++ b/public/assets/javascripts/ui/site/EditProfileModal.js @@ -6,7 +6,7 @@ var EditProfileModal = ModalFormView.extend({ load: function(){ this.reset() - $.get("/api/profile", $.proxy(function(data){ + $.get("/api/profile", function(data){ console.log(data) for (var i in data) { @@ -23,7 +23,7 @@ var EditProfileModal = ModalFormView.extend({ } this.show() - }, this)) + }.bind(this)) }, validate: function(){ diff --git a/public/assets/javascripts/ui/site/EditProjectModal.js b/public/assets/javascripts/ui/site/EditProjectModal.js index 356d8b7..20baa54 100644 --- a/public/assets/javascripts/ui/site/EditProjectModal.js +++ b/public/assets/javascripts/ui/site/EditProjectModal.js @@ -31,7 +31,7 @@ var EditProjectModal = ModalView.extend({ var fields = this.$form.serializeArray() var request = $.post(this.action, $.param(fields)); - request.done($.proxy(function (response) { + request.done(function (response) { if (response.error) { this.$errors.show(); for (var key in response.error.errors) { @@ -42,7 +42,7 @@ var EditProjectModal = ModalView.extend({ else { window.location.href = "/profile" } - }, this)); + }.bind(this)); } }) diff --git a/public/assets/javascripts/ui/site/LayoutsModal.js b/public/assets/javascripts/ui/site/LayoutsModal.js index 46ed634..4948b0e 100644 --- a/public/assets/javascripts/ui/site/LayoutsModal.js +++ b/public/assets/javascripts/ui/site/LayoutsModal.js @@ -3,23 +3,30 @@ var LayoutsIndex = View.extend({ initialize: function(){ this.$templates = this.$(".templates") + this.$noTemplates = this.$(".no-templates") + this.$form = this.$("form") }, load: function(type){ this.$templates.children("span").remove() - $.get(this.action, $.proxy(this.populate, this)) + $.get(this.action, this.populate.bind(this)) }, populate: function(data){ - data.forEach($.proxy(function(room){ + if (! data.length) { + this.$templates.hide() + this.$form.hide() + this.$noTemplates.show() + } + data.forEach(function(room){ var $span = $("<span>") // $span.html(JSON.stringify(room)) $span.data("slug", room.slug) $span.css("background-image", "url(" + room.photo + ")") this.$templates.append($span) - }, this)) + }.bind(this)) this.show() } @@ -28,7 +35,7 @@ var LayoutsIndex = View.extend({ var ProjectsModal = ModalView.extend(LayoutsIndex.prototype).extend({ el: ".mediaDrawer.projects", - action: "/api/projects", + action: "/api/project", events: { "click .templates span": 'toggleActive', @@ -65,7 +72,7 @@ var ProjectsModal = ModalView.extend(LayoutsIndex.prototype).extend({ var LayoutsModal = ModalView.extend(LayoutsIndex.prototype).extend({ el: ".mediaDrawer.layouts", - action: "/api/layouts", + action: "/api/layout", events: { "click .templates span": 'toggleActive', @@ -93,7 +100,7 @@ var LayoutsModal = ModalView.extend(LayoutsIndex.prototype).extend({ var NewProjectModal = ModalView.extend(LayoutsIndex.prototype).extend({ el: ".mediaDrawer.newProject", - action: "/api/layouts", + action: "/api/layout", events: { "click .templates span": 'toggleActive', diff --git a/public/assets/javascripts/ui/site/PasswordForgot.js b/public/assets/javascripts/ui/site/PasswordForgot.js new file mode 100644 index 0000000..ecbfc07 --- /dev/null +++ b/public/assets/javascripts/ui/site/PasswordForgot.js @@ -0,0 +1,26 @@ +var PasswordForgot = ModalFormView.extend({ + el: ".mediaDrawer.passwordForgot", + action: "/auth/forgotPassword", + + validate: function(){ + var errors = [] + + var email = this.$("#emailInput").val() + + if (! email.length) { + errors.push("Please enter your email address"); + } + if (email.indexOf("@") === -1) { + errors.push("Sorry, that is not a valid email address"); + } + + return errors + }, + + success: function(res){ + AlertModal.alert("Check your email, you should receive further instructions momentarily.", function(e){ + window.location.href = "/" + }) + }, + +}) diff --git a/public/assets/javascripts/ui/site/PasswordReset.js b/public/assets/javascripts/ui/site/PasswordReset.js new file mode 100644 index 0000000..9b87d37 --- /dev/null +++ b/public/assets/javascripts/ui/site/PasswordReset.js @@ -0,0 +1,31 @@ +var PasswordReset = ModalFormView.extend({ + el: ".mediaDrawer.passwordReset", + action: "/auth/password", + + load: function(){ + var opt = JSON.parse( $("#opt").html() ) + this.$("[name=nonce]").val( opt.nonce ) + this.__super__.load.call(this) + }, + + validate: function(){ + var errors = [] + + var pw1 = this.$("#passwordInput1").val() + var pw2 = this.$("#passwordInput2").val() + + if (! pw1.length) { + errors.push("Please enter a password"); + } + if (pw1 !== pw2) { + errors.push("Passwords don't match"); + } + + return errors + }, + + success: function(res){ + window.location.href = "/profile" + } + +}) diff --git a/public/assets/javascripts/ui/site/ProfileView.js b/public/assets/javascripts/ui/site/ProfileView.js new file mode 100644 index 0000000..8661bcd --- /dev/null +++ b/public/assets/javascripts/ui/site/ProfileView.js @@ -0,0 +1,28 @@ + +var ProfileView = View.extend({ + + initialize: function() { + }, + + load: function() { + var classes = ['one', 'two', 'three', 'four', + 'five', 'six', 'seven', 'eight', + 'nine', 'ten', 'eleven', 'twelve', + 'thirteen']; + $(".bio").addClass(choice(classes)); + + $("td.border").each(function() { + var iframe = $(this).find("iframe").get('0') + if (! iframe) return + $(this).on({ + mouseenter: function(e){ + iframe.contentWindow.postMessage("spin-on", window.location.origin) + }, + mouseleave: function(e){ + iframe.contentWindow.postMessage("spin-off", window.location.origin) + } + }) + }) + } + +}) diff --git a/public/assets/javascripts/ui/site/UsernameTaken.js b/public/assets/javascripts/ui/site/UsernameTaken.js new file mode 100644 index 0000000..fc9f50a --- /dev/null +++ b/public/assets/javascripts/ui/site/UsernameTaken.js @@ -0,0 +1,27 @@ +var UsernameTaken = ModalFormView.extend({ + el: ".mediaDrawer.usernameTaken", + action: "/auth/usernameTaken", + + load: function(){ + var opt = JSON.parse( $("#opt").html() ) + this.$("#usernameThatIsTaken").html( opt.username ) + this.__super__.load.call(this) + }, + + validate: function(){ + var errors = [] + + var username = this.$("#usernameInput").val() + + if (! username.length) { + errors.push("Please enter a username"); + } + + return errors + }, + + success: function(res){ + window.location.href = "/profile" + } + +}) |
