summaryrefslogtreecommitdiff
path: root/public/assets/javascripts/ui
diff options
context:
space:
mode:
Diffstat (limited to 'public/assets/javascripts/ui')
-rw-r--r--public/assets/javascripts/ui/_router.js129
-rw-r--r--public/assets/javascripts/ui/blueprint/BlueprintEditor.js129
-rw-r--r--public/assets/javascripts/ui/blueprint/BlueprintInfo.js92
-rw-r--r--public/assets/javascripts/ui/blueprint/BlueprintNotice.js62
-rw-r--r--public/assets/javascripts/ui/blueprint/BlueprintScaler.js158
-rw-r--r--public/assets/javascripts/ui/blueprint/BlueprintSettings.js123
-rw-r--r--public/assets/javascripts/ui/blueprint/BlueprintToolbar.js97
-rw-r--r--public/assets/javascripts/ui/blueprint/BlueprintUploader.js147
-rw-r--r--public/assets/javascripts/ui/blueprint/BlueprintView.js106
-rw-r--r--public/assets/javascripts/ui/builder/BuilderInfo.js101
-rw-r--r--public/assets/javascripts/ui/builder/BuilderSettings.js9
-rw-r--r--public/assets/javascripts/ui/builder/BuilderToolbar.js4
-rw-r--r--public/assets/javascripts/ui/builder/BuilderView.js13
-rw-r--r--public/assets/javascripts/ui/editor/Collaborators.js1
-rw-r--r--public/assets/javascripts/ui/editor/ColorControl.js153
-rw-r--r--public/assets/javascripts/ui/editor/EditorSettings.js109
-rw-r--r--public/assets/javascripts/ui/editor/EditorToolbar.js154
-rw-r--r--public/assets/javascripts/ui/editor/EditorView.js54
-rw-r--r--public/assets/javascripts/ui/editor/HelpCursor.js79
-rw-r--r--public/assets/javascripts/ui/editor/MediaEditor.js63
-rw-r--r--public/assets/javascripts/ui/editor/MediaTumblr.js53
-rw-r--r--public/assets/javascripts/ui/editor/MediaUpload.js19
-rw-r--r--public/assets/javascripts/ui/editor/MediaViewer.js193
-rw-r--r--public/assets/javascripts/ui/editor/Presets.js122
-rw-r--r--public/assets/javascripts/ui/editor/SculptureEditor.js237
-rw-r--r--public/assets/javascripts/ui/editor/TextEditor.js238
-rw-r--r--public/assets/javascripts/ui/editor/WallpaperPicker.js229
-rw-r--r--public/assets/javascripts/ui/lib/AlertModal.js38
-rw-r--r--public/assets/javascripts/ui/lib/AnimatedView.js31
-rw-r--r--public/assets/javascripts/ui/lib/ConfirmModal.js46
-rw-r--r--public/assets/javascripts/ui/lib/ErrorModal.js38
-rw-r--r--public/assets/javascripts/ui/lib/FormView.js227
-rw-r--r--public/assets/javascripts/ui/lib/LabColorPicker.js (renamed from public/assets/javascripts/ui/editor/LightControl.js)184
-rw-r--r--public/assets/javascripts/ui/lib/ModalView.js79
-rw-r--r--public/assets/javascripts/ui/lib/Parser.js118
-rw-r--r--public/assets/javascripts/ui/lib/Router.js1
-rw-r--r--public/assets/javascripts/ui/lib/ToggleableView.js19
-rw-r--r--public/assets/javascripts/ui/lib/Toolbar.js22
-rw-r--r--public/assets/javascripts/ui/lib/UploadView.js152
-rw-r--r--public/assets/javascripts/ui/lib/View.js231
-rw-r--r--public/assets/javascripts/ui/reader/EmbedView.js76
-rw-r--r--public/assets/javascripts/ui/reader/MediaPlayer.js28
-rw-r--r--public/assets/javascripts/ui/reader/ReaderView.js88
-rw-r--r--public/assets/javascripts/ui/reader/ShareView.js62
-rw-r--r--public/assets/javascripts/ui/reader/Tracker.js133
-rw-r--r--public/assets/javascripts/ui/reader/_router.js24
-rw-r--r--public/assets/javascripts/ui/site/EditProfileModal.js9
-rw-r--r--public/assets/javascripts/ui/site/EditSubscriptionModal.js290
-rw-r--r--public/assets/javascripts/ui/site/HomeView.js28
-rw-r--r--public/assets/javascripts/ui/site/LayoutsIndex.js85
-rw-r--r--public/assets/javascripts/ui/site/LayoutsModal.js183
-rw-r--r--public/assets/javascripts/ui/site/NewProjectModal.js118
-rw-r--r--public/assets/javascripts/ui/site/ProfileView.js28
-rw-r--r--public/assets/javascripts/ui/site/ProjectList.js64
-rw-r--r--public/assets/javascripts/ui/site/StaffView.js70
-rw-r--r--public/assets/javascripts/ui/z_share.js25
56 files changed, 4444 insertions, 927 deletions
diff --git a/public/assets/javascripts/ui/_router.js b/public/assets/javascripts/ui/_router.js
index 794079e..61b1d1b 100644
--- a/public/assets/javascripts/ui/_router.js
+++ b/public/assets/javascripts/ui/_router.js
@@ -9,6 +9,7 @@ var SiteRouter = Router.extend({
"click [data-role='new-project-modal']": 'newProject',
"click [data-role='edit-project-modal']": 'editProject',
"click [data-role='edit-profile-modal']": 'editProfile',
+ "click [data-role='edit-subscription-modal']": 'editSubscription',
"click [data-role='new-document-modal']": 'newDocument',
"click [data-role='edit-document-modal']": 'editDocument',
"click [data-role='destroy-document-modal']": 'destroyDocument',
@@ -17,36 +18,55 @@ var SiteRouter = Router.extend({
},
routes: {
+ "/": 'home',
+ "/home": 'home',
+ "/login": 'signin',
+ "/signin": 'signin',
+ "/signup": 'signup',
+
+ "/auth/usernameTaken": 'usernameTaken',
+ "/auth/password": 'passwordReset',
+ "/auth/forgotPassword": 'passwordForgot',
+
+ "/profile": 'profile',
+ "/profile/edit": 'editProfile',
+ "/profile/billing": 'editSubscription',
+ "/profile/:name": 'profile',
+ "/about/:name/edit": 'editDocument',
+ "/about/new": 'newDocument',
+
+ "/layout": 'layoutPicker',
+ "/layout/:name": 'layoutEditor',
+
+ "/blueprint": 'blueprintEditor',
+ "/blueprint/:name": 'blueprintEditor',
+
+ "/project": 'projectPicker',
+ "/project/new": 'newProject',
+ "/project/blueprint/:blueprint": 'projectNewWithBlueprint',
+ "/project/new/:layout": 'projectNewWithLayout',
+ "/project/:name": 'projectViewer',
+ "/project/:name/edit": 'projectEditor',
+ "/project/:name/view": 'projectViewer',
+
+ "/test/blueprint": 'blueprintEditor',
+ },
+
+ mobileRoutes: {
"/": 'home',
+ "/home": 'home',
"/login": 'signin',
+ "/signin": 'signin',
"/signup": 'signup',
-
"/auth/usernameTaken": 'usernameTaken',
"/auth/password": 'passwordReset',
"/auth/forgotPassword": 'passwordForgot',
"/profile": 'profile',
"/profile/edit": 'editProfile',
+ "/profile/billing": 'editSubscription',
"/profile/:name": 'profile',
- "/about/:name/edit": 'editDocument',
- "/about/new": 'newDocument',
-
- "/layout": 'layoutPicker',
- "/layout/:name": 'layoutEditor',
- "/project": 'projectPicker',
- "/project/new": 'newProject',
- "/project/new/:layout": 'projectNewWithLayout',
- "/project/:name": 'projectViewer',
- "/project/:name/edit": 'projectEditor',
- "/project/:name/view": 'projectViewer',
-
- "/test/wallpaper": 'testWallpaper',
- },
-
- mobileRoutes: {
- "/": 'home',
- "/profile": 'profile',
"/project/:name": 'projectViewer',
},
@@ -58,6 +78,7 @@ var SiteRouter = Router.extend({
this.newProjectModal = new NewProjectModal()
this.editProjectModal = new EditProjectModal()
this.editProfileModal = new EditProfileModal()
+ this.editSubscriptionModal = new EditSubscriptionModal()
this.passwordForgotModal = new PasswordForgot()
this.documentModal = new DocumentModal()
this.profileView = new ProfileView()
@@ -65,16 +86,22 @@ var SiteRouter = Router.extend({
this.route()
if (is_mobile) {
- $(".topLinks").hide()
- $(".share").hide()
+ // $(".topLinks").hide()
+ // $(".share").hide()
+ $('.projectItem').each(function(){
+ this.href = this.href.replace(/\/edit$/, "")
+ })
}
- $("body").removeClass("loading")
+ setTimeout(function(){
+ $("body").removeClass("loading")
+ }, 200)
},
layoutEditor: function(e, name){
app.mode.builder = true
app.launch()
+ if (app.unsupported) return
this.builderView = app.controller = new BuilderView()
this.builderView.load(name)
@@ -97,12 +124,29 @@ var SiteRouter = Router.extend({
window.history.pushState(null, document.title, "/project/new")
this.newProjectModal.load()
},
+
+ projectNewWithBlueprint: function(e, blueprint){
+ e && e.preventDefault()
+
+ Rooms.shapesMode = true
+
+ app.mode.editor = true
+ app.launch()
+ if (app.unsupported) return
+
+ blueprint = slugify(blueprint)
+
+ window.history.pushState(null, document.title, "/project/blueprint/" + blueprint)
+ this.editorView = app.controller = new EditorView()
+ this.editorView.loadBlueprint(blueprint)
+ },
projectNewWithLayout: function(e, layout){
e && e.preventDefault()
app.mode.editor = true
app.launch()
+ if (app.unsupported) return
layout = slugify(layout)
@@ -123,6 +167,7 @@ var SiteRouter = Router.extend({
projectEditor: function(e, name){
app.mode.editor = true
app.launch()
+ if (app.unsupported) return
this.editorView = app.controller = new EditorView()
this.editorView.load(name)
@@ -131,10 +176,20 @@ var SiteRouter = Router.extend({
projectViewer: function(e, name){
app.mode.editor = true
app.launch()
+ if (app.unsupported) return
this.readerView = app.controller = new ReaderView()
this.readerView.load(name)
},
+
+ blueprintEditor: function(e, name){
+ environment.init = environment.minimal
+ app.launch()
+ if (app.unsupported) return
+
+ this.blueprintView = app.controller = new BlueprintView ()
+ this.blueprintView.load(name)
+ },
signup: function(e){
e && e.preventDefault()
@@ -177,7 +232,12 @@ var SiteRouter = Router.extend({
this.editProfileModal.load()
},
+ editSubscription: function(e){
+ e && e.preventDefault()
+ window.history.pushState(null, document.title, "/profile/billing")
+ this.editSubscriptionModal.load()
+ },
newDocument: function(e){
e && e.preventDefault()
@@ -212,30 +272,5 @@ var SiteRouter = Router.extend({
// 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/blueprint/BlueprintEditor.js b/public/assets/javascripts/ui/blueprint/BlueprintEditor.js
new file mode 100644
index 0000000..7704689
--- /dev/null
+++ b/public/assets/javascripts/ui/blueprint/BlueprintEditor.js
@@ -0,0 +1,129 @@
+
+var wallHeight = 180
+var shapes = new ShapeList
+var last_point = new vec2 (0,0)
+
+var BlueprintEditor = View.extend(AnimatedView.prototype).extend({
+
+ regions: [],
+
+ initialize: function(opt){
+ this.parent = opt.parent
+
+ $(window).resize(this.resize.bind(this))
+
+ scene = new MX.Scene().addTo("#perspective")
+ scene.camera.radius = 20
+ cam = scene.camera
+
+ scene.width = window.innerWidth/2
+ scene.height = window.innerHeight
+ scene.perspective = window.innerHeight
+
+ movements = new MX.Movements(cam, viewHeight)
+ movements.init()
+ movements.lock()
+
+ app.on("move", function(pos){
+ cam.x = pos.x
+ cam.y = pos.y
+ cam.z = pos.z
+ })
+
+ var floorplan = this.floorplan = new MX.Image({
+ backface: true,
+ })
+ scene.add(this.floorplan)
+
+ // recenter perspective view by rightclicking map
+ this.floorplan.el.addEventListener("contextmenu", function(e){
+ e.preventDefault()
+ var offset = offsetFromPoint(e, this)
+ var x = (offset.left - 0.5) * floorplan.width * floorplan.scale
+ var z = (offset.top - 0.5) * floorplan.height * floorplan.scale
+ controls.opt.center.x = -x
+ controls.opt.center.y = 0
+ controls.opt.center.z = z
+ }, true)
+
+ scene.update()
+
+ controls = new MX.OrbitCamera({
+ el: scene.el,
+ radius: 3000,
+ radiusRange: [ 10, 10000 ],
+ rotationX: PI/4,
+ rotationY: PI/2,
+ })
+ controls.init()
+ },
+
+ resize: function(){
+ if (this.parent.orbiting) {
+ scene.width = window.innerWidth/2
+ scene.height = window.innerHeight
+ this.parent.map.resize( window.innerWidth/2, window.innerHeight )
+ this.parent.map.canvas.style.display = "block"
+ }
+ else {
+ scene.width = window.innerWidth
+ scene.height = window.innerHeight
+ this.parent.map.canvas.style.display = "none"
+ }
+ },
+
+ loadFloorplan: function(media){
+ // console.log(media)
+ this.floorplan.load({
+ media: media,
+ keepImage: true,
+ rotationX: -PI/2,
+ rotationY: PI,
+ scale: media.scale,
+ })
+ this.startAnimating()
+ this.regions = RegionList.buildByShape()
+ },
+
+ animate: function(t, dt){
+ map.update(t)
+
+ movements.update(dt)
+ controls.update()
+ scene.update()
+
+ map.draw.ctx.save()
+ map.draw.translate()
+
+ this.floorplan.draw(map.draw.ctx, true)
+
+ map.draw.coords()
+
+ if (shapes.workline) {
+ shapes.workline.draw(map.draw.ctx, "rgba(255,255,0,0.1)", "#f80")
+ if (map.ui.placing && last_point) {
+ shapes.workline.draw_line( map.draw.ctx, last_point )
+ }
+ }
+
+ shapes.draw(map.draw.ctx, "rgba(255,255,0,0.1)", "#f80")
+
+ map.draw.ctx.strokeStyle = "#f00";
+ map.draw.x_at( this.parent.startPosition )
+ map.draw.mouse(map.ui.mouse.cursor)
+ map.draw.camera(scene.camera)
+
+// var colors = ["rgba(0,0,0,0.1)"]
+// var colors = ["rgba(255,255,255,1)"]
+
+// map.draw.regions(this.regions, colors, "#000")
+
+// this.regions.forEach(function(room,i){
+// map.draw.ctx.fillStyle = colors[i % colors.length]
+// map.draw.ctx.fillRect( room.x.a, room.y.a, room.width(), room.height() )
+// })
+
+ map.draw.ctx.restore()
+ },
+
+})
diff --git a/public/assets/javascripts/ui/blueprint/BlueprintInfo.js b/public/assets/javascripts/ui/blueprint/BlueprintInfo.js
new file mode 100644
index 0000000..51b310e
--- /dev/null
+++ b/public/assets/javascripts/ui/blueprint/BlueprintInfo.js
@@ -0,0 +1,92 @@
+
+var BlueprintInfo = View.extend({
+ el: "#blueprintInfo",
+
+ events: {
+ "mousedown": "stopPropagation",
+ "keydown": 'stopPropagation',
+ "change [name=height]": 'changeHeight',
+ "keydown [name=height]": 'enterHeight',
+ "change [name=units]": 'changeUnits',
+ "keydown [name=viewHeight]": 'enterViewHeight',
+ "change [name=viewHeight]": 'changeViewHeight',
+ "click .openScaler": 'openScaler',
+ },
+
+ initialize: function(opt){
+ this.parent = opt.parent
+ this.$height = this.$("[name=height]")
+ this.$units = this.$("[name=units]")
+ this.$viewHeight = this.$("[name=viewHeight]")
+ this.$unitName = this.$(".unitName")
+ this.$blueprintScaleDisplay = this.$("#blueprintScaleDisplay")
+ },
+
+ load: function(data){
+ this.$viewHeight.unitVal( window.viewHeight = data.viewHeight || app.defaults.viewHeight )
+ this.$height.unitVal( window.wallHeight = data.wallHeight || app.defaults.wallHeight )
+ this.$units.val( data.units )
+ this.$('span.units').html( data.units )
+ this.$unitName.html( data.units )
+
+ var resolution
+ switch (data.units) {
+ case 'ft':
+ resolution = app.defaults.footResolution
+ break
+ case 'm':
+ resolution = app.defaults.meterResolution
+ break
+ case 'px':
+ default:
+ resolution = 1
+ break
+ }
+ this.$blueprintScaleDisplay.html( ((1/data.scale) * resolution).toFixed(1) )
+ this.show()
+ },
+
+ toggle: function(state){
+ this.$el.toggleClass("active", state)
+ this.$viewHeight.unitVal( window.viewHeight )
+ },
+
+ openScaler: function(){
+ this.parent.scaler.pick( this.parent.data, true )
+ this.parent.scaler.show()
+ },
+
+ show: function(){
+ this.toggle(true)
+ },
+
+ hide: function(){
+ this.toggle(false)
+ },
+
+ deselect: function(){
+ this.toggle(true)
+ },
+
+ enterHeight: function(e){
+ if (e.keyCode == 13) this.changeHeight(e)
+ },
+ changeHeight: function(e){
+ e.stopPropagation()
+ window.wallHeight = this.$height.unitVal()
+ shapes.forEach(function(line){
+ line.mx.set_height( window.wallHeight )
+ })
+ },
+ changeUnits: function(){
+ app.units = this.$units.val()
+ this.$('.units').resetUnitVal()
+ },
+ enterViewHeight: function(e){
+ if (e.keyCode == 13) this.changeViewHeight(e)
+ },
+ changeViewHeight: function(){
+ window.viewHeight = this.$viewHeight.unitVal()
+ }
+
+})
diff --git a/public/assets/javascripts/ui/blueprint/BlueprintNotice.js b/public/assets/javascripts/ui/blueprint/BlueprintNotice.js
new file mode 100644
index 0000000..4b799a6
--- /dev/null
+++ b/public/assets/javascripts/ui/blueprint/BlueprintNotice.js
@@ -0,0 +1,62 @@
+var BlueprintNotice = View.extend(ToggleableView.prototype).extend({
+
+ el: "#blueprintNotice",
+
+ events: {
+ "click .next": "next",
+ },
+
+ initialize: function(opt){
+ this.parent = opt.parent
+ this.$notice = this.$(".notice")
+ this.$next = this.$(".next")
+ },
+
+ notice: function(msg){
+ this.$notice.html(msg)
+ },
+
+ showCreateProjectNotice: function(){
+ this.notice("<a href='/project/blueprint/" + this.parent.data.slug +
+ "'>Start a new project</a> with this blueprint.")
+ this.$next.hide()
+ this.nextFn = null
+ this.show()
+ },
+
+ showStartPositionNotice: function(){
+ this.parent.settings.hide()
+ this.notice("First, click the map to set the starting location.")
+ this.$next.show().html("Next")
+ this.show()
+ this.nextFn = this.showStartAngleNotice.bind(this)
+ },
+
+ showStartAngleNotice: function(){
+ this.parent.settings.hide()
+ this.notice("Next, rotate the camera to the desired orientation.")
+ this.$next.show().html("Done")
+ this.show()
+ this.nextFn = this.doneSettingPosition.bind(this)
+ this.parent.toolbar.toggleOrbitMode(false)
+ },
+
+ doneSettingPosition: function(){
+ this.nextFn = null
+ this.$next.hide()
+ this.hide()
+ this.parent.settings.show()
+ this.parent.startPosition.rotationX = cam.rotationX
+ this.parent.startPosition.rotationY = cam.rotationY
+ this.parent.toolbar.toggleOrbitMode(true)
+ this.parent.toolbar.orthoPolylineMode()
+ },
+
+ nextFn: null,
+ next: function(){
+ if (this.nextFn) {
+ this.nextFn()
+ }
+ },
+
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/ui/blueprint/BlueprintScaler.js b/public/assets/javascripts/ui/blueprint/BlueprintScaler.js
new file mode 100644
index 0000000..cd370ef
--- /dev/null
+++ b/public/assets/javascripts/ui/blueprint/BlueprintScaler.js
@@ -0,0 +1,158 @@
+
+var BlueprintScaler = ModalFormView.extend(AnimatedView.prototype).extend({
+ el: ".blueprintScaler",
+
+ fixedClose: true,
+
+ action: "/api/blueprint/scale",
+
+ events: {
+ "change [name=blueprint-dimensions]": "changeDimensions",
+ "change [name=blueprint-units]": "changeUnits",
+ "click .uploadNewBlueprint": "showUploader",
+ },
+
+ initialize: function(opt){
+ this.parent = opt.parent
+
+ this.$blueprintMap = this.$("#blueprintMap")
+ this.$blueprintDimensionsRapper = this.$("#blueprintDimensions")
+ this.$dimensions = this.$("[name=blueprint-dimensions]")
+ this.$pixels = this.$("[name=blueprint-pixels]")
+ this.$units = this.$("[name=blueprint-units]")
+ this.$save = this.$("#saveBlueprint")
+
+ this.map = new Map ({
+ type: "ortho",
+ el: this.$blueprintMap.get(0),
+ width: window.innerWidth,
+ height: window.innerHeight,
+ zoom: -2,
+ zoom_min: -7.0,
+ zoom_max: 2,
+ })
+ this.lineTool = new LineTool
+ this.map.ui.add_tool("line", this.lineTool)
+ this.map.ui.set_tool("line")
+
+ scene = scene || { camera: { x: 0, y: 0, z: 0 } }
+
+ this.floorplan = new MX.Image ()
+ },
+
+ showUploader: function(){
+ this.parent.uploader.show()
+ },
+
+ pick: function(media, shouldEdit){
+ this.media = media
+
+ this.floorplan.load({ media: media, scale: 1, keepImage: true })
+
+ if (!! media.units && ! shouldEdit) {
+ this.parent.ready(media)
+ this.hide()
+ this.stopAnimating()
+ return
+ }
+
+ if (media.units && media.line && media.scale) {
+ var points = media.line.split(",")
+ this.lineTool.line[0] = new vec2( +points[0], +points[1] )
+ this.lineTool.line[1] = new vec2( +points[2], +points[3] )
+
+ app.units = media.units
+ this.$units.val( media.units )
+ this.$dimensions.unitVal( media.scale * this.lineLength() )
+ }
+
+ this.startAnimating()
+ },
+
+ animate: function(t, dt){
+ this.map.update(t)
+
+ this.map.draw.ctx.save()
+ this.map.draw.translate()
+
+ this.floorplan.draw(this.map.draw.ctx, true)
+
+ this.map.draw.ctx.save()
+ this.map.draw.ctx.strokeStyle = "#f00"
+ this.map.draw.ctx.lineWidth = 1/map.zoom
+ switch (this.lineTool.line.length) {
+ case 1:
+ this.map.draw.line(
+ this.lineTool.line[0].a,
+ this.lineTool.line[0].b,
+ this.lineTool.cursor.x.a,
+ this.lineTool.cursor.y.a
+ )
+ break
+ case 2:
+ this.map.draw.line(
+ this.lineTool.line[0].a,
+ this.lineTool.line[0].b,
+ this.lineTool.line[1].a,
+ this.lineTool.line[1].b
+ )
+ break
+ }
+ this.map.draw.ctx.restore()
+
+ this.map.draw.coords()
+
+ this.map.draw.mouse(this.map.ui.mouse.cursor)
+
+ this.map.draw.ctx.restore()
+ },
+
+ changeDimensions: function(){
+ app.units = this.$units.val()
+ this.$dimensions.unitVal()
+ },
+ changeUnits: function(){
+ app.units = this.$units.val()
+ this.$dimensions.resetUnitVal()
+ },
+ lineLength: function(){
+ if (this.lineTool.line.length !== 2) return 0
+ var line = this.lineTool.line
+ return dist( line[0].a, line[0].b, line[1].a, line[1].b )
+ },
+
+ validate: function(){
+ var val = this.$dimensions.unitVal()
+ var errors = []
+ if (! this.lineLength()) {
+ errors.push("no line")
+ this.$dimensions.val("")
+ alert("Please click two corners of a wall and then specify how long it is in feet or meters.")
+ }
+ else if (val == 0) {
+ errors.push("no measurement")
+ alert("Please tell us how long the wall is in feet or meters.")
+ }
+ return errors
+ },
+
+ showErrors: function(){},
+
+ serialize: function(){
+ var fd = new FormData(), line = this.lineTool.line
+ fd.append( "_id", this.media._id)
+ fd.append( "units", this.$units.val() )
+ fd.append( "scale", this.$dimensions.unitVal() / this.lineLength() )
+ fd.append( "line", [line[0].a,line[0].b,line[1].a,line[1].b].join(",") )
+ fd.append( "_csrf", $("[name=_csrf]").val())
+ return fd
+ },
+
+ success: function(){
+ this.media.scale = this.$dimensions.unitVal() / this.lineLength()
+ this.stopAnimating()
+ this.parent.ready(this.media)
+ this.hide()
+ },
+
+})
diff --git a/public/assets/javascripts/ui/blueprint/BlueprintSettings.js b/public/assets/javascripts/ui/blueprint/BlueprintSettings.js
new file mode 100644
index 0000000..8addb9c
--- /dev/null
+++ b/public/assets/javascripts/ui/blueprint/BlueprintSettings.js
@@ -0,0 +1,123 @@
+
+var BlueprintSettings = FormView.extend(ToggleableView.prototype).extend({
+ el: "#blueprintSettings",
+
+ action: "/api/blueprint/edit",
+ destroyAction: "/api/blueprint/destroy",
+
+ events: {
+ "mousedown": "stopPropagation",
+ "keydown": 'stopPropagation',
+ "keydown [name=name]": 'enterSubmit',
+ "click [data-role='save-layout']": 'clickSave',
+ "click [data-role='clear-layout']": 'clear',
+ "click [data-role='destroy-layout']": 'destroy',
+ },
+
+ initialize: function(opt){
+ this.parent = opt.parent
+ this.__super__.initialize.call(this)
+
+ this.$id = this.$("[name=_id]")
+ this.$csrf = this.$("[name=_csrf]")
+ this.$name = this.$("[name=name]")
+ },
+
+ load: function(data){
+ this.$id.val(data._id)
+ if (data.name) {
+ this.$name.val(data.name)
+ this.hide()
+ }
+ else {
+ this.$name.val("")
+ }
+ if (data.shapes) {
+ shapes.destroy()
+ shapes.deserialize( data.shapes )
+ shapes.build()
+ }
+ },
+
+ clear: function(){
+ shapes.destroy()
+ },
+
+ destroy: function(){
+ var msg = "Are you sure you want to delete the blueprint " + sanitize(this.$name.val()) + "?"
+ ConfirmModal.confirm(msg, function(){
+ $.ajax({
+ url: this.destroyAction,
+ type: "delete",
+ data: { _id: this.$id.val(), _csrf: this.$csrf.val() },
+ success: function(data){
+ window.location.href = "/layout"
+ }
+ })
+ }.bind(this))
+ },
+
+ enterSubmit: function (e) {
+ e.stopPropagation()
+ var base = this
+ if (e.keyCode == 13) {
+ setTimeout(function(){ base.save(e) }, 100)
+ }
+ },
+
+ validate: function(){
+ var errors = []
+ var name = this.$name.val()
+ if (! name || ! name.length) {
+ errors.push("Blueprint needs a name.")
+ }
+ if (shapes.count() == 0) {
+ errors.push("Please add some walls.")
+ }
+ return errors
+ },
+
+ showErrors: function(errors){
+ var $errors = $("<span>")
+ errors.forEach(function(err){
+ var $row = $("<div>")
+ $row.html(err)
+ $errors.append( $row )
+ })
+ ErrorModal.alert($errors)
+ },
+
+ serialize: function(){
+ var fd = new FormData()
+ fd.append( "_csrf", this.$csrf.val() )
+ fd.append( "_id", this.$id.val() )
+ fd.append( "name", this.$name.val() )
+ fd.append( "shapes", JSON.stringify( shapes.serialize() ) )
+ fd.append( "startPosition", JSON.stringify( this.parent.quantizeStartPosition() ) )
+ fd.append( "wallHeight", this.parent.info.$height.unitVal() )
+ fd.append( "units", this.parent.info.$units.val() )
+ return fd
+ },
+
+ clickSave: function(){
+ this.toggle(false)
+ this.save()
+ },
+
+ success: function(data){
+ this.parent.data = data
+
+ this.$id.val(data._id)
+ this.$name.val(data.name)
+ this.action = this.updateAction
+
+ this.hide()
+ this.parent.notice.showCreateProjectNotice()
+
+ Minotaur.unwatch(this)
+ Minotaur.hide()
+
+ window.history.pushState(null, document.title, "/blueprint/" + data.slug)
+ },
+
+})
diff --git a/public/assets/javascripts/ui/blueprint/BlueprintToolbar.js b/public/assets/javascripts/ui/blueprint/BlueprintToolbar.js
new file mode 100644
index 0000000..458357d
--- /dev/null
+++ b/public/assets/javascripts/ui/blueprint/BlueprintToolbar.js
@@ -0,0 +1,97 @@
+var BlueprintToolbar = View.extend({
+
+ el: "#blueprintToolbar",
+
+ events: {
+ "click [data-role=upload-floorplan]": 'showUploader',
+ "click [data-role=toggle-orbit-mode]": 'toggleOrbitMode',
+ "click [data-role=arrow-mode]": 'arrowMode',
+ "click [data-role=polyline-mode]": 'polylineMode',
+ "click [data-role=ortho-polyline-mode]": 'orthoPolylineMode',
+ "click [data-role=eraser-mode]": 'eraserMode',
+ "click [data-role=start-position-mode]": 'startPositionMode',
+ "click [data-role=toggle-layout-settings]": 'toggleSettings',
+ },
+
+ initialize: function(opt){
+ this.parent = opt.parent
+
+ this.$modes = this.$('.mode')
+ this.$toggleOrbitMode = this.$('[data-role=toggle-orbit-mode]')
+ this.$arrowMode = this.$('[data-role=arrow-mode]')
+ this.$polylineMode = this.$('[data-role=polyline-mode]')
+ this.$orthoPolylineMode = this.$('[data-role=ortho-polyline-mode]')
+ this.$eraserMode = this.$('[data-role=eraser-mode]')
+ this.$startPositionMode = this.$('[data-role=start-position-mode]')
+
+ keys.on('escape', function(){
+ app.controller.toolbar.toggleOrbitMode()
+ })
+
+ this.arrowMode()
+ },
+
+ showUploader: function(){
+ this.parent.scaler.show()
+ this.parent.uploader.show()
+ },
+
+ toggleOrbitMode: function(state){
+ this.parent.orbiting = typeof state == "boolean" ? state : ! this.parent.orbiting
+ this.$toggleOrbitMode.toggleClass("inuse", ! this.parent.orbiting)
+ this.parent.editor.resize()
+ if (this.parent.orbiting) {
+ controls.toggle(true)
+ movements.lock()
+ }
+ else {
+ controls.toggle(false)
+ movements.unlock()
+ movements.gravity(true)
+ var pos = this.parent.quantizeStartPosition()
+ cam.rotationX = pos.rotationX
+ cam.rotationY = pos.rotationY
+ cam.x = pos.x
+ cam.y = viewHeight
+ cam.z = pos.z
+ }
+ },
+
+ toggleSettings: function(){
+ this.parent.settings.toggle()
+ this.parent.notice.toggle( ! this.parent.data.isNew && ! this.parent.settings.visible() )
+ },
+
+ setActiveMode: function( $el ) {
+ this.$modes.removeClass('active')
+ $el.addClass('active')
+ },
+
+ arrowMode: function(){
+ this.setActiveMode( this.$arrowMode )
+ this.parent.map.ui.set_tool("arrow")
+ },
+
+ polylineMode: function(){
+ this.setActiveMode( this.$polylineMode )
+ this.parent.map.ui.set_tool("polyline")
+ },
+
+ orthoPolylineMode: function(){
+ this.setActiveMode( this.$orthoPolylineMode )
+ this.parent.map.ui.set_tool("ortho-polyline")
+ },
+
+ eraserMode: function(){
+ this.setActiveMode( this.$eraserMode )
+ this.parent.map.ui.set_tool("eraser")
+ },
+
+ startPositionMode: function(){
+ this.setActiveMode( this.$startPositionMode )
+ this.parent.map.ui.set_tool("start-position")
+ this.parent.settings.hide()
+ this.parent.notice.showStartPositionNotice()
+ },
+
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/ui/blueprint/BlueprintUploader.js b/public/assets/javascripts/ui/blueprint/BlueprintUploader.js
new file mode 100644
index 0000000..aa62a4c
--- /dev/null
+++ b/public/assets/javascripts/ui/blueprint/BlueprintUploader.js
@@ -0,0 +1,147 @@
+
+var BlueprintUploader = UploadView.extend({
+ el: ".blueprintUploader",
+
+ mediaTag: "blueprint",
+ createAction: "/api/blueprint/new",
+ uploadAction: "/api/blueprint/upload",
+ listAction: "/api/blueprint/user",
+ destroyAction: "/api/blueprint/destroy",
+
+ events: {
+ "mousedown": 'stopPropagation',
+ "change .url": "enterUrl",
+ "keydown .url": "enterSetUrl",
+
+ "click .blueprint": "pick",
+ "click .remove": "destroy",
+ },
+
+ initialize: function(opt){
+ this.parent = opt.parent
+ this.__super__.initialize.call(this)
+
+ this.$url = this.$(".url")
+ this.$blueprints = this.$(".blueprints")
+ },
+
+ loaded: false,
+ nameToShow: null,
+ load: function(name){
+ this.nameToShow = name || ""
+ $.get(this.listAction, { tag: this.mediaTag }, this.populate.bind(this))
+ },
+
+ populate: function(data){
+ this.loaded = true
+ if (data && data.length) {
+ this.$blueprints.show()
+ data.forEach(this.append.bind(this))
+ if (this.nameToShow === "new") {
+ // don't pick anything..
+ this.show()
+ }
+ else if (! this.nameToShow) {
+ this.hide()
+ data.some(function(el){
+ if (el.slug == this.nameToShow) {
+ this.parent.scaler.pick(el)
+ return true
+ }
+ }.bind(this))
+ }
+ else {
+ this.hide()
+ this.parent.scaler.pick(data[0])
+ }
+ }
+ else {
+ this.parent.scaler.hideClose()
+ this.show()
+ }
+ },
+
+ pick: function(e){
+ var $el = $(e.currentTarget)
+ var media = $el.data("media")
+ this.hide()
+ this.parent.scaler.pick(media)
+ if (media.slug) {
+ window.history.pushState(null, document.title, "/blueprint/" + media.slug)
+ }
+ },
+
+ destroy: function(e){
+ e.stopPropagation()
+ var $el = $(e.currentTarget)
+ var _id = $el.closest(".blueprint").data("id")
+ $el.remove()
+ $.ajax({
+ type: "delete",
+ url: this.destroyAction,
+ data: { _id: _id, _csrf: $("[name=_csrf]").val() }
+ }).complete(function(){
+ })
+ },
+
+ show: function(){
+ this.toggle(true)
+ },
+ hide: function(){
+ this.toggle(false)
+ },
+ toggle: function (state) {
+ this.$el.toggleClass("active", state)
+ },
+
+ addUrl: function (url){
+ Parser.loadImage(url, function(media){
+ if (! media) return
+ media._csrf = $("[name=_csrf]").val()
+ media.tag = this.mediaTag
+
+ var request = $.ajax({
+ type: "post",
+ url: this.createAction,
+ data: media,
+ })
+ request.done(this.add.bind(this))
+
+ }.bind(this))
+ },
+ enterUrl: function(){
+ var url = this.$url.sanitize()
+ this.addUrl(url)
+ this.$url.val("")
+ },
+ enterSetUrl: function (e) {
+ e.stopPropagation()
+ if (e.keyCode == 13) {
+ setTimeout(this.enterUrl.bind(this), 100)
+ }
+ },
+
+ add: function(media){
+ this.$blueprints.show()
+ this.append(media)
+ this.hide()
+ this.parent.scaler.pick(media, true)
+ },
+
+ append: function(media){
+ var $el = $("<span>")
+ var img = new Image ()
+ img.src = media.url
+ var remove = document.createElement("span")
+ remove.className = "remove"
+ remove.innerHTML = "<span>x</span>"
+
+ $el.data("id", media._id)
+ $el.data("media", media)
+ $el.append(img)
+ $el.append(remove)
+ $el.addClass("blueprint")
+ this.$blueprints.append($el)
+ },
+
+})
diff --git a/public/assets/javascripts/ui/blueprint/BlueprintView.js b/public/assets/javascripts/ui/blueprint/BlueprintView.js
new file mode 100644
index 0000000..1858c3d
--- /dev/null
+++ b/public/assets/javascripts/ui/blueprint/BlueprintView.js
@@ -0,0 +1,106 @@
+
+var BlueprintView = View.extend({
+ el: "#blueprintView",
+
+ action: "/api/blueprint/show/",
+
+ events: {
+ },
+
+ initialize: function(){
+// this.colorControl = new ColorControl ({ parent: this })
+// this.cursor = new HelpCursor({ parent: this })
+ this.map = this.buildMap()
+ this.editor = new BlueprintEditor ({ parent: this })
+ this.toolbar = new BlueprintToolbar ({ parent: this })
+ this.uploader = new BlueprintUploader ({ parent: this })
+ this.scaler = new BlueprintScaler ({ parent: this })
+ this.info = new BlueprintInfo ({ parent: this })
+ this.settings = new BlueprintSettings ({ parent: this })
+ this.notice = new BlueprintNotice ({ parent: this })
+ Rooms.shapesMode = true
+ },
+
+ load: function(name){
+ name = sanitize(name) || "new"
+ this.uploader.load(name)
+// name = sanitize(name)
+// $.get(this.action + name, this.ready.bind(this))
+ },
+
+ orbiting: true,
+ startPosition: {},
+ quantizeStartPosition: function(){
+ //
+ var regions = RegionList.build()
+ var pos = this.startPosition
+ var startPositionIsInARoom = regions.some(function(region){
+ return region.contains(pos.x, pos.z)
+ })
+ if (startPositionIsInARoom) {
+ return this.startPosition
+ }
+ else if (! regions.length) {
+ return {
+ x: 0,
+ y: viewHeight,
+ z: 0,
+ rotationX: 0,
+ rotationY: Math.PI/2,
+ }
+ }
+ else {
+ var center = regions[0].center()
+ return {
+ x: center.a,
+ y: viewHeight,
+ z: center.b,
+ rotationX: 0,
+ rotationY: Math.PI/2,
+ }
+ }
+ },
+
+ buildMap: function(){
+ // i forget if this has to be global
+ map = new Map ({
+ type: "ortho",
+ el: document.querySelector("#orthographic"),
+ width: window.innerWidth/2,
+ height: window.innerHeight,
+ zoom: -2,
+ zoom_min: -6.2,
+ zoom_max: 1,
+ })
+ map.ui.add_tool("arrow", new ArrowTool)
+ map.ui.add_tool("polyline", new PolylineTool)
+ map.ui.add_tool("ortho-polyline", new OrthoPolylineTool)
+ map.ui.add_tool("eraser", new EraserTool)
+ map.ui.add_tool("position", new PositionTool)
+ map.ui.add_tool("start-position", new StartPositionTool)
+ map.ui.placing = false
+ return map
+ },
+
+ ready: function(data){
+ this.data = data
+ this.info.load(data)
+ this.settings.load(data)
+ this.editor.loadFloorplan(data)
+ if (! data.shapes || data.shapes.length == 0) {
+ this.startPosition = { x: 0, y: 0, z: 0, rotationX: 0, rotationY: Math.PI/2 }
+ }
+ else {
+ this.startPosition = data.startPosition
+ }
+ this.notice.hide()
+ this.settings.show()
+ },
+
+ hideExtras: function(){
+ },
+
+ pickWall: function(wall, pos){
+ },
+
+})
diff --git a/public/assets/javascripts/ui/builder/BuilderInfo.js b/public/assets/javascripts/ui/builder/BuilderInfo.js
index e6b7e97..aa58d6e 100644
--- a/public/assets/javascripts/ui/builder/BuilderInfo.js
+++ b/public/assets/javascripts/ui/builder/BuilderInfo.js
@@ -3,15 +3,21 @@ var BuilderInfo = View.extend({
el: "#builderInfo",
events: {
+ "mousedown": "stopPropagation",
"keydown": 'stopPropagation',
"change [name=x]": 'changeX',
"change [name=y]": 'changeY',
"change [name=width]": 'changeWidth',
"change [name=depth]": 'changeDepth',
"change [name=height]": 'changeHeight',
+ "keydown [name=width]": 'enterWidth',
+ "keydown [name=depth]": 'enterDepth',
+ "keydown [name=height]": 'enterHeight',
"change [name=units]": 'changeUnits',
- "change [name=resolution]": 'changeResolution',
+ "keydown [name=viewHeight]": 'enterViewHeight',
"change [name=viewHeight]": 'changeViewHeight',
+ "change [name=heightGlobal]": 'changeHeightGlobal',
+ "click [data-role=destroy-room]": 'destroy',
},
initialize: function(opt){
@@ -24,25 +30,51 @@ var BuilderInfo = View.extend({
this.$units = this.$("[name=units]")
this.$viewHeight = this.$("[name=viewHeight]")
this.$unitName = this.$(".unitName")
+ this.$noSelection = this.$(".no-selection")
+ this.$settings = this.$(".setting")
+ this.$heightGlobalCheckbox = this.$("[name=heightGlobal]")
app.on("builder-pick-room", this.pick.bind(this))
- app.on("builder-destroy-room", this.destroy.bind(this))
+ app.on("builder-destroy-room", this.hide.bind(this))
+ app.on("builder-pick-nothing", this.deselect.bind(this))
},
load: function(data){
- this.$viewHeight.unitVal( data.viewHeight || app.defaults.viewHeight )
- this.$units.val( "ft" )
- this.$unitName.html( "ft" )
+ this.$viewHeight.unitVal( window.viewHeight = data.viewHeight || app.defaults.viewHeight )
+ this.$units.val( data.units || "ft" )
+ this.$unitName.html( data.units || "ft" )
+
+ if (Rooms.regions.length == 0) {
+ this.changeHeightGlobal(true)
+ }
+ else {
+ var rooms = Rooms.values()
+ var height = rooms[0].height
+ var differentHeights = Rooms.some(function(room){
+ return room.height != height
+ })
+ this.changeHeightGlobal( ! differentHeights )
+ }
},
toggle: function(state){
+ this.$settings.toggle( !! this.room )
+ this.$noSelection.toggle( ! this.room )
this.$el.toggleClass("active", state)
+ if (state) {
+ this.$viewHeight.unitVal( window.viewHeight )
+ this.parent.cursor.message("builder")
+ }
+ else {
+ this.parent.cursor.message("start")
+ }
},
show: function(){
this.toggle(true)
},
- hide: function(){
+ hide: function(){
+ this.room = null
this.toggle(false)
},
@@ -57,43 +89,84 @@ var BuilderInfo = View.extend({
this.$y.unitVal( room.rect.y.a )
this.show()
},
+
+ deselect: function(){
+ this.room = null
+ this.toggle(true)
+ },
+
+ destroy: function(){
+ UndoStack.push({
+ type: "destroy-room",
+ undo: this.room.copy(),
+ redo: { id: this.room.id },
+ })
+
+ Rooms.remove(this.room)
+ app.tube("builder-destroy-room", this.room)
- destroy: function(room){
this.room = null
this.hide()
},
+ enterWidth: function(e){
+ if (e.keyCode == 13) this.changeWidth(e)
+ },
changeWidth: function(e){
e.stopPropagation()
this.room.rect.x.setLength( this.$width.unitVal() )
- Rooms.clipper.update()
+ Rooms.rebuild()
+ },
+
+ enterDepth: function(e){
+ if (e.keyCode == 13) this.changeDepth(e)
},
changeDepth: function(e){
e.stopPropagation()
this.room.rect.y.setLength( this.$depth.unitVal() )
- Rooms.clipper.update()
+ Rooms.rebuild()
+ },
+
+ enterHeight: function(e){
+ if (e.keyCode == 13) this.changeHeight(e)
},
changeHeight: function(e){
e.stopPropagation()
- this.room.height = this.$height.unitVal()
- Rooms.clipper.update()
+ var height = this.room.height = this.$height.unitVal()
+ if (window.heightIsGlobal) {
+ Rooms.forEach(function(room){
+ room.height = height
+ })
+ }
+ Rooms.rebuild()
},
changeX: function(e){
e.stopPropagation()
this.room.rect.x.setPosition( this.$x.unitVal() )
- Rooms.clipper.update()
+ Rooms.rebuild()
},
changeY: function(e){
e.stopPropagation()
this.room.rect.y.setPosition( this.$y.unitVal() )
- Rooms.clipper.update()
+ Rooms.rebuild()
},
changeUnits: function(){
app.units = this.$units.val()
this.$('.units').resetUnitVal()
},
- changeViewHeight: function(){
+ enterViewHeight: function(e){
+ if (e.keyCode == 13) this.changeViewHeight(e)
+ },
+ changeViewHeight: function(){
window.viewHeight = this.$viewHeight.unitVal( )
},
+ changeHeightGlobal: function(state){
+ if (typeof state == "boolean") {
+ this.$heightGlobalCheckbox.prop("checked", state)
+ window.heightIsGlobal = state
+ return
+ }
+ window.heightIsGlobal = this.$heightGlobalCheckbox.prop("checked")
+ },
})
diff --git a/public/assets/javascripts/ui/builder/BuilderSettings.js b/public/assets/javascripts/ui/builder/BuilderSettings.js
index 94eed29..256bffe 100644
--- a/public/assets/javascripts/ui/builder/BuilderSettings.js
+++ b/public/assets/javascripts/ui/builder/BuilderSettings.js
@@ -7,6 +7,7 @@ var BuilderSettings = FormView.extend({
destroyAction: "/api/layout/destroy",
events: {
+ "mousedown": "stopPropagation",
"keydown": 'stopPropagation',
"keydown [name=name]": 'enterSubmit',
"click [data-role='save-layout']": 'clickSave',
@@ -29,7 +30,7 @@ var BuilderSettings = FormView.extend({
this.$id.val(data._id)
this.$name.val(data.name)
- this.parent.lightControl.loadDefaults()
+ this.parent.colorControl.loadDefaults()
data.rooms && Rooms.deserialize(data.rooms)
data.startPosition && scene.camera.move(data.startPosition)
@@ -51,7 +52,7 @@ var BuilderSettings = FormView.extend({
this.$name.val( names.join(" ") )
this.action = this.createAction
- window.history.pushState(null, document.title, "/builder/new")
+ window.history.pushState(null, document.title, "/layout/new")
},
clear: function(){
@@ -107,7 +108,7 @@ var BuilderSettings = FormView.extend({
},
serialize: function(){
- map.draw.render()
+ var thumbnail = map.draw.render()
var fd = new FormData()
fd.append( "_csrf", this.$csrf.val() )
fd.append( "_id", this.$id.val() )
@@ -115,7 +116,7 @@ var BuilderSettings = FormView.extend({
fd.append( "privacy", this.$privacy.filter(":checked").val() == "private" )
fd.append( "rooms", JSON.stringify( Rooms.serialize() ) )
fd.append( "startPosition", JSON.stringify( app.position(scene.camera) ) )
- fd.append( "thumbnail", dataUriToBlob(map.canvas.toDataURL()) )
+ fd.append( "thumbnail", dataUriToBlob(thumbnail.toDataURL()) )
return fd
},
diff --git a/public/assets/javascripts/ui/builder/BuilderToolbar.js b/public/assets/javascripts/ui/builder/BuilderToolbar.js
index 2eb7590..e9dcce3 100644
--- a/public/assets/javascripts/ui/builder/BuilderToolbar.js
+++ b/public/assets/javascripts/ui/builder/BuilderToolbar.js
@@ -3,6 +3,7 @@ var BuilderToolbar = View.extend({
el: "#builderToolbar",
events: {
+ "mousedown": "stopPropagation",
"click [data-role='toggle-map-view']": 'toggleMap',
"click [data-role='toggle-layout-settings']": 'toggleSettings',
"click [data-role='undo']": 'undo',
@@ -59,6 +60,9 @@ var BuilderToolbar = View.extend({
var state = map.ui.permissions.toggle("destroy")
$(".inuse").removeClass("inuse")
$(e.currentTarget).toggleClass("inuse", state)
+ if (! state) {
+ this.resetPermissions()
+ }
},
})
diff --git a/public/assets/javascripts/ui/builder/BuilderView.js b/public/assets/javascripts/ui/builder/BuilderView.js
index 81dce52..b975499 100644
--- a/public/assets/javascripts/ui/builder/BuilderView.js
+++ b/public/assets/javascripts/ui/builder/BuilderView.js
@@ -11,12 +11,8 @@ var BuilderView = View.extend({
this.info = new BuilderInfo ({ parent: this })
this.toolbar = new BuilderToolbar ({ parent: this })
this.settings = new BuilderSettings ({ parent: this })
- this.lightControl = new LightControl ({ parent: this })
-
- app.on("rooms-built", function(){
- Walls.paint()
- })
-
+ this.colorControl = new ColorControl ({ parent: this })
+ this.cursor = new HelpCursor({ parent: this })
},
load: function(name){
@@ -36,6 +32,9 @@ var BuilderView = View.extend({
},
hideExtras: function(){
- }
+ },
+
+ pickWall: function(wall, pos){
+ },
})
diff --git a/public/assets/javascripts/ui/editor/Collaborators.js b/public/assets/javascripts/ui/editor/Collaborators.js
index 452ad15..b57510d 100644
--- a/public/assets/javascripts/ui/editor/Collaborators.js
+++ b/public/assets/javascripts/ui/editor/Collaborators.js
@@ -9,6 +9,7 @@ var Collaborators = ModalFormView.extend({
destroyAction: function(){ return "/api/collaborator/" + this.parent.data.slug + "/destroy" },
events: {
+ "mousedown": "stopPropagation",
"keydown [name=email]": "enterSubmit",
"click [data-role=destroy-collaborator]": "destroy",
},
diff --git a/public/assets/javascripts/ui/editor/ColorControl.js b/public/assets/javascripts/ui/editor/ColorControl.js
new file mode 100644
index 0000000..54a6a2e
--- /dev/null
+++ b/public/assets/javascripts/ui/editor/ColorControl.js
@@ -0,0 +1,153 @@
+
+var ColorControl = View.extend({
+ el: ".colorcontrol",
+
+ events: {
+ "mousedown": "stopPropagation",
+ "click .color-swatches span": "setSurface",
+ "click .colors span": "setHue",
+ },
+
+ colors: [
+ [255,94,58],
+ [255,149,0],
+ [255,219,76],
+ [76,217,100],
+ [52,170,220],
+ [29,98,240],
+ [198,68,252],
+ [0,0,0],
+ [74,74,74],
+ [125,126,127],
+ [209,211,212],
+ [235,235,235],
+ [255,255,255],
+ ],
+
+ initialize: function(opt){
+ this.parent = opt.parent
+
+ this.colorPicker = new LabColorPicker(this, 155, 155)
+ this.$(".color-picker").append( this.colorPicker.canvas )
+ this.$(".color-picker").append( this.colorPicker.cursor )
+ this.$(".slider").append( this.colorPicker.brightness )
+
+ this.$swatches = this.$(".swatch")
+ this.$labels = this.$(".swatch + label")
+ this.$swatch = {
+ wall: this.$("#wall-color"),
+ outline: this.$("#outline-color"),
+ floor: this.$("#floor-color"),
+ ceiling: this.$("#ceiling-color"),
+ }
+
+ this.$colors = this.$(".colors")
+ this.colors.forEach(function(color){
+ var $swatch = $("<span>")
+ $swatch.css("background-color","rgb(" + color + ")")
+ $swatch.data('color', color)
+ this.$colors.append($swatch)
+ }.bind(this))
+
+ if ($.browser.mozilla) {
+ $("#floor-color").parent().hide()
+ $("#ceiling-color").parent().hide()
+ }
+ },
+
+ modes: [ "wall", "outline", "floor", "ceiling" ],
+
+ load: function(data){
+ this.modes.forEach(function(mode){
+ Walls.setColor[mode](data[mode])
+ this.$swatch[ mode ].css("background-color", rgb_string(data[mode]))
+ }.bind(this))
+ this.setMode("wall")
+ },
+
+ loadDefaults: function(){
+ var colors = {
+ wall: app.defaults.colors.wall.slice(),
+ outline: app.defaults.colors.outline.slice(),
+ floor: app.defaults.colors.floor.slice(),
+ ceiling: app.defaults.colors.ceiling.slice(),
+ }
+ this.load(colors)
+ },
+
+ toggle: function(state){
+ if (state) {
+ this.parent.cursor.message("colors")
+ }
+ this.$el.toggleClass("active", state);
+ },
+
+ show: function(){
+ this.toggle(true)
+ },
+
+ hide: function(){
+ this.toggle(false)
+ },
+
+ pickColor: function(rgb, Lab){
+ this.labColor = Lab
+ this.setSwatchColor(this.mode, rgb)
+ // console.log(rgb)
+ Walls.setColor[ this.mode ](rgb)
+ this.parent.presets.modified = true
+ },
+
+ setSwatchColor: function(mode, rgb) {
+ this.$swatch[ mode ].css("background-color", rgb_string(rgb))
+ },
+
+ initialState: null,
+
+ begin: function(){
+ this.initialState = this.serialize()
+ },
+
+ serialize: function(){
+ return {
+ mode: this.mode,
+ rgb: Walls.colors[ this.mode ]
+ }
+ },
+
+ finalize: function(){
+ if (! this.initialState) { return }
+ UndoStack.push({
+ type: 'update-colors',
+ undo: this.initialState,
+ redo: this.serialize(),
+ })
+
+ this.initialState = null
+
+ // TODO: watch individual wall object here
+ Minotaur.watch( app.router.editorView.settings )
+ },
+
+ setMode: function (mode) {
+ var color, brightness
+ this.mode = mode
+ this.$(".active").removeClass("active")
+ this.$swatch[ mode ].parent().addClass("active")
+ color = Walls.colors[ mode ]
+
+ this.labColor = this.colorPicker.load(color)
+ },
+
+ setSurface: function(e){
+ var mode = $('.swatch', e.currentTarget).data('mode')
+ this.setMode(mode)
+ },
+
+ setHue: function(e){
+ var color = $(e.currentTarget).data('color')
+ this.labColor = this.colorPicker.load(color)
+ this.pickColor(color, this.labColor)
+ }
+
+})
diff --git a/public/assets/javascripts/ui/editor/EditorSettings.js b/public/assets/javascripts/ui/editor/EditorSettings.js
index 8a88f7a..5aa88e9 100644
--- a/public/assets/javascripts/ui/editor/EditorSettings.js
+++ b/public/assets/javascripts/ui/editor/EditorSettings.js
@@ -5,8 +5,11 @@ var EditorSettings = FormView.extend({
createAction: "/api/project/new",
updateAction: "/api/project/edit",
destroyAction: "/api/project/destroy",
+
+ useMinotaur: true,
events: {
+ "mousedown": "stopPropagation",
"keydown": 'stopPropagation',
"keydown [name=name]": 'enterSubmit',
"click [data-role='show-collaborators']": 'showCollaborators',
@@ -14,6 +17,12 @@ var EditorSettings = FormView.extend({
"click [data-role='clone-project']": 'clone',
"click [data-role='clear-project']": 'clear',
"click [data-role='destroy-project']": 'destroy',
+ "click [data-role='toggle-map']": 'toggleMap',
+ "click [data-role='view-project']": 'viewProject',
+ "click #startText": "setStartPosition",
+ "click #moveText": "confirmStartPosition",
+ "click #confirmText": "setStartPosition",
+ "click #goText": "goToStartPosition",
},
initialize: function(opt){
@@ -25,21 +34,30 @@ var EditorSettings = FormView.extend({
this.$name = this.$("[name=name]")
this.$description = this.$("[name=description]")
this.$privacy = this.$("[name=privacy]")
+ this.$startPoint = this.$("#startpoint")
},
load: function(data){
this.action = data.isNew ? this.createAction : this.updateAction
this.parent.data = data
- data.rooms && Rooms.deserialize(data.rooms)
- data.walls && Walls.deserialize(data.walls)
- data.startPosition && scene.camera.move(data.startPosition)
+ if (data.shapes && data.shapes.length) {
+ Rooms.deserializeFromShapes(data, data.walls)
+ }
+ else if (data.rooms) {
+ Rooms.deserialize(data.rooms, data.walls)
+ }
+ if (data.startPosition) {
+ scene.camera.move(data.startPosition)
+ this.startPosition = data.startPosition
+ this.$startPoint.addClass("confirmed")
+ }
if (data.colors && data.colors.wall) {
- this.parent.lightControl.load(data.colors)
+ this.parent.colorControl.load(data.colors)
}
else {
- this.parent.lightControl.loadDefaults()
+ this.parent.colorControl.loadDefaults()
}
if (data.walls) {
@@ -53,7 +71,7 @@ var EditorSettings = FormView.extend({
}
if (data.isNew) {
- this.$name.val( "Untitled Room" )
+ this.$name.val( "Untitled" )
}
else {
this.thumbnailIsStale()
@@ -64,6 +82,7 @@ var EditorSettings = FormView.extend({
data.privacy && this.$privacy.find("[value=" + data.privacy + "]").prop("checked", "checked")
data.media && Scenery.deserialize(data.media)
+ data.sculpture && Sculpture.deserialize(data.sculpture)
}
},
@@ -72,7 +91,14 @@ var EditorSettings = FormView.extend({
this.parent.collaborators.show()
},
- clone: function(){
+ toggleMap: function(e){
+ e.preventDefault()
+ app.controller.toolbar.toggleMap()
+ },
+
+ clone: function(e){
+ e.preventDefault()
+
var names = this.$name.val().split(" ")
if ( ! isNaN(Number( names[names.length-1] )) ) {
names[names.length-1] = Number( names[names.length-1] ) + 1
@@ -84,12 +110,15 @@ var EditorSettings = FormView.extend({
this.$id.val('new')
this.$name.val( names.join(" ") )
this.action = this.createAction
+ this.thumbnailState = null
- window.history.pushState(null, document.title, "/builder/new")
+ window.history.pushState(null, document.title, "/project/new")
},
- clear: function(){
- Rooms.removeAll()
+ clear: function(e){
+ e.preventDefault()
+ Scenery.removeAll()
+ Sculpture.removeAll()
},
destroy: function(){
@@ -100,7 +129,7 @@ var EditorSettings = FormView.extend({
type: "delete",
data: { _id: this.$id.val(), _csrf: this.$csrf.val() },
success: function(data){
- window.location.href = "/project"
+ window.location.href = "/profile"
}
})
}.bind(this))
@@ -109,8 +138,12 @@ var EditorSettings = FormView.extend({
toggle: function(state){
var state = typeof state == 'boolean' ? state : ! this.$el.hasClass("active")
this.$el.toggleClass("active", state)
-
+
+ $(".inuse").removeClass("inuse")
$("[data-role='toggle-project-settings']").toggleClass("inuse", state)
+ if (state) {
+ this.parent.cursor.message("settings")
+ }
},
enterSubmit: function (e) {
@@ -140,22 +173,43 @@ var EditorSettings = FormView.extend({
ErrorModal.alert($errors)
},
+ startPosition: null,
+ setStartPosition: function(){
+ this.$startPoint.addClass("active").removeClass("confirmed")
+ },
+ confirmStartPosition: function(){
+ this.$startPoint.removeClass("active").addClass("confirmed")
+ this.startPosition = app.position(scene.camera)
+ },
+ goToStartPosition: function(){
+ if (! this.startPosition) return
+ scene.camera.move(this.startPosition)
+ },
+
serialize: function(){
- map.draw.render()
var fd = new FormData()
fd.append( "_csrf", this.$csrf.val() )
fd.append( "_id", this.$id.val() )
fd.append( "name", this.$name.val() )
fd.append( "description", this.$description.val() )
fd.append( "privacy", this.$privacy.filter(":checked").val() == "private" )
- fd.append( "rooms", JSON.stringify( Rooms.serialize() ) )
+ fd.append( "viewHeight", window.viewHeight )
+ if (Rooms.shapesMode) {
+ fd.append( "shapes", JSON.stringify( shapes.serialize() ) )
+ }
+ else {
+ fd.append( "rooms", JSON.stringify( Rooms.serialize() ) )
+ }
fd.append( "walls", JSON.stringify( Walls.serialize() ) )
fd.append( "colors", JSON.stringify( Walls.colors ) )
fd.append( "media", JSON.stringify( Scenery.serialize() ) )
- fd.append( "startPosition", JSON.stringify( app.position(scene.camera) ) )
+ fd.append( "sculpture", JSON.stringify( Sculpture.serialize() ) )
+ fd.append( "startPosition", JSON.stringify( this.startPosition || false ) )
+ fd.append( "lastPosition", JSON.stringify( app.position(scene.camera) ) )
if (this.thumbnailIsStale()) {
- fd.append( "thumbnail", dataUriToBlob(map.canvas.toDataURL()) )
+ var thumbnail = map.draw.render()
+ fd.append( "thumbnail", dataUriToBlob(thumbnail.toDataURL()) )
}
return fd
},
@@ -174,6 +228,16 @@ var EditorSettings = FormView.extend({
clickSave: function(){
this.toggle(false)
this.save()
+ this.isVisible = true
+ },
+
+ viewAfterSave: false,
+ viewProject: function(e){
+ e.preventDefault()
+ Minotaur.unwatch(this)
+ Minotaur.hide()
+ this.viewAfterSave = true
+ this.save()
},
success: function(data){
@@ -181,11 +245,22 @@ var EditorSettings = FormView.extend({
this.$name.val(data.name)
this.action = this.updateAction
+ if (this.viewAfterSave) {
+ window.location.pathname = "/project/" + data.slug
+ return
+ }
+
Minotaur.unwatch(this)
Minotaur.hide()
window.history.pushState(null, document.title, "/project/" + data.slug + "/edit")
-
+
+ this.parent.share.setLink( "http://vvalls.com/project/" + data.slug )
+ if (this.isVisible) {
+ this.isVisible = false
+ this.parent.share.show()
+ }
+
this.parent.data = data
},
diff --git a/public/assets/javascripts/ui/editor/EditorToolbar.js b/public/assets/javascripts/ui/editor/EditorToolbar.js
index e91da0f..a5ad2dd 100644
--- a/public/assets/javascripts/ui/editor/EditorToolbar.js
+++ b/public/assets/javascripts/ui/editor/EditorToolbar.js
@@ -3,86 +3,86 @@ var EditorToolbar = View.extend({
el: "#editorToolbar",
events: {
+ "mousedown": 'stopPropagation',
+ "mouseenter": 'mouseenter',
+ "mouseleave": 'mouseleave',
+ "click [data-role='undo']": 'undo',
"click [data-role='toggle-map-view']": 'toggleMap',
"click [data-role='toggle-project-settings']": 'toggleSettings',
"click [data-role='open-media-viewer']": 'openMediaViewer',
- "click [data-role='resize-media']": 'resizeMedia',
- "click [data-role='destroy-media']": 'destroyMedia',
+ "click [data-role='toggle-presets']": 'togglePresets',
"click [data-role='toggle-wallpaper-panel']": 'toggleWallpaper',
- "click [data-role='toggle-light-control']": 'toggleLightControl',
- "click [data-role='edit-wall-text']": 'editWallText',
+ "click [data-role='toggle-color-control']": 'toggleColorControl',
+ "click [data-role='toggle-text-editor']": 'toggleTextEditor',
},
initialize: function(opt){
this.parent = opt.parent
},
- toggleMap: function(){
- map.toggle()
- // $("#minimap").toggleClass("hide");
+ undo: function(e){
+ if (e.shiftKey) {
+ var canRedo = UndoStack.redo()
+ console.log("can redo", canRedo)
+ }
+ else {
+ var canUndo = UndoStack.undo()
+ console.log("can undo", canUndo)
+ }
+ },
+
+ toggleMap: function(state){
+// if (typeof state != "boolean") {
+// state = ! $("[data-role='toggle-map-view']").hasClass("inuse")
+// this.resetControls()
+// }
+// $("[data-role='toggle-map-view']").toggleClass("inuse", state)
+ var state = map.toggle(state)
+ if (state) { map.ui.blur() }
+ $("#minimap").toggleClass("hide", state)
+ this.parent.info.toggle(state)
},
toggleSettings: function(){
- this.resetMode()
- $(".inuse").removeClass("inuse")
- this.parent.lightControl.hide()
+// this.resetMode()
+ this.toggleMap(false)
+ this.parent.textEditor.hide()
+ this.parent.presets.hide()
+ this.parent.colorControl.hide()
this.parent.wallpaperPicker.hide()
this.parent.mediaEditor.hide()
this.parent.settings.toggle()
},
openMediaViewer: function(){
- this.parent.mediaViewer.show()
- this.parent.mediaUpload.show()
this.resetMode()
this.resetControls()
+ this.toggleMap(false)
+ this.parent.mediaViewer.show()
},
-
+
resetMode: function(){
- this.resizeMedia(false)
- this.destroyMedia(false)
+ $(".inuse").removeClass("inuse")
+ $("body").removeClass("addText")
+ this.parent.hideExtras()
+ this.resetPermissions()
},
resetControls: function(){
+ $(".inuse").removeClass("inuse")
+ this.toggleMap(false)
+ this.parent.textEditor.hide()
this.parent.wallpaperPicker.hide()
- this.parent.lightControl.hide()
- },
-
- resizeMedia: function(e, state){
- this.resetControls()
- if (! state && typeof e == "boolean") {
- state = e
- editor.permissions.assign("resize", state)
- }
- else {
- state = editor.permissions.toggle("resize")
- }
- ! state && editor.permissions.assign("move", true)
- $(".inuse").removeClass("inuse")
- $("[data-role='resize-media']").toggleClass("inuse", state)
- if (state) {
- if (this.parent.mediaEditor.scenery) {
- Scenery.resize.show( this.parent.mediaEditor.scenery )
- }
- }
- else {
- Scenery.resize.hide()
- }
+ this.parent.presets.hide()
+ this.parent.colorControl.hide()
+ this.parent.settings.hide()
},
- destroyMedia: function(e, state){
- this.resetControls()
- if (! state && typeof e == "boolean") {
- state = e
- editor.permissions.assign("destroy", state)
- }
- else {
- state = editor.permissions.toggle("destroy")
- }
- ! state && editor.permissions.assign("move", true)
- $(".inuse").removeClass("inuse")
- $("[data-role='destroy-media']").toggleClass("inuse", state)
- $("body").toggleClass("destroyActive", state)
+ resetPermissions: function(){
+ editor.permissions.add("pick")
+ editor.permissions.add("move")
+ editor.permissions.add("resize")
+ editor.permissions.remove("destroy")
},
toggleWallpaper: function(){
@@ -90,31 +90,67 @@ var EditorToolbar = View.extend({
this.resetMode()
$("[data-role='toggle-wallpaper-panel']").toggleClass("inuse", state)
this.parent.mediaEditor.hide()
- this.parent.lightControl.hide()
+ this.parent.colorControl.hide()
+ this.parent.textEditor.hide()
this.parent.settings.hide()
+ this.parent.presets.hide()
+ this.toggleMap(false)
this.parent.wallpaperPicker.toggle(state)
},
- toggleLightControl: function(){
- var state = ! $("[data-role='toggle-light-control']").hasClass("inuse")
+ toggleColorControl: function(){
+ var state = ! $("[data-role='toggle-color-control']").hasClass("inuse")
this.resetMode()
- $("[data-role='toggle-light-control']").toggleClass("inuse", state)
+ $("[data-role='toggle-color-control']").toggleClass("inuse", state)
this.parent.mediaEditor.hide()
this.parent.wallpaperPicker.hide()
+ this.parent.textEditor.hide()
this.parent.settings.hide()
- this.parent.lightControl.toggle(state)
+ this.parent.presets.hide()
+ this.toggleMap(false)
+ this.parent.colorControl.toggle(state)
},
- editWallText: function(){
- },
+ toggleTextEditor: function(){
+ var state = ! $("[data-role='toggle-text-editor']").hasClass("inuse")
+ this.resetMode()
+ $("[data-role='toggle-text-editor']").toggleClass("inuse", state)
+ this.parent.mediaEditor.hide()
+ this.parent.wallpaperPicker.hide()
+ this.parent.colorControl.hide()
+ this.parent.settings.hide()
+ this.parent.presets.hide()
+ this.toggleMap(false)
+ this.parent.textEditor.toggle(state)
+ },
+ togglePresets: function(){
+ var state = ! $("[data-role='toggle-presets']").hasClass("inuse")
+ this.resetMode()
+ $("[data-role='toggle-presets']").toggleClass("inuse", state)
+ this.parent.mediaEditor.hide()
+ this.parent.wallpaperPicker.hide()
+ this.parent.textEditor.hide()
+ this.parent.settings.hide()
+ this.parent.colorControl.hide()
+ this.toggleMap(false)
+ this.parent.presets.toggle(state)
+ },
+
+ mouseenter: function(){
+ this.parent.cursor.hide()
+ },
+
+ mouseleave: function(){
+ this.parent.cursor.show()
+ },
})
var editor = new function(){
this.permissions = new Permissions({
'pick': true,
'move': true,
- 'resize': false,
+ 'resize': true,
'destroy': false,
})
} \ No newline at end of file
diff --git a/public/assets/javascripts/ui/editor/EditorView.js b/public/assets/javascripts/ui/editor/EditorView.js
index e11f189..c05b373 100644
--- a/public/assets/javascripts/ui/editor/EditorView.js
+++ b/public/assets/javascripts/ui/editor/EditorView.js
@@ -2,6 +2,7 @@
var EditorView = View.extend({
el: "#editorView",
+ blueprintAction: "/api/blueprint/user/",
projectAction: "/api/project/",
layoutAction: "/api/layout/",
@@ -9,14 +10,21 @@ var EditorView = View.extend({
},
initialize: function(){
+ this.cursor = new HelpCursor ({ parent: this })
this.toolbar = new EditorToolbar ({ parent: this })
this.settings = new EditorSettings ({ parent: this })
+ this.info = new BuilderInfo ({ parent: this })
this.mediaViewer = new MediaViewer ({ parent: this })
this.mediaUpload = new MediaUpload ({ parent: this })
+ this.mediaTumblr = new MediaTumblr ({ parent: this })
this.mediaEditor = new MediaEditor ({ parent: this })
+ this.sculptureEditor = new SculptureEditor ({ parent: this })
this.wallpaperPicker = new WallpaperPicker ({ parent: this })
- this.lightControl = new LightControl ({ parent: this })
+ this.colorControl = new ColorControl ({ parent: this })
+ this.textEditor = new TextEditor ({ parent: this })
this.collaborators = new Collaborators ({ parent: this })
+ this.presets = new Presets ({ parent: this })
+ this.share = new ShareView ({ parent: this })
},
load: function(name){
@@ -25,27 +33,65 @@ var EditorView = View.extend({
},
loadLayout: function(layout){
+ if (layout == "empty") {
+ this.readyLayout({})
+ this.toolbar.toggleMap()
+ return
+ }
layout = sanitize(layout)
$.get(this.layoutAction + layout, this.readyLayout.bind(this))
},
+ loadBlueprint: function(blueprint){
+ $.get(this.blueprintAction + blueprint, this.readyLayout.bind(this))
+ },
+
ready: function(data){
$("#map").hide()
-
+
+ this.data = data
+
this.settings.load(data)
+ this.info.load(data)
},
readyLayout: function(data){
data.isNew = true
+ // $('#help-button').trigger("click")
this.ready(data)
},
pick: function(scenery){
- this.mediaEditor.pick(scenery)
+ if (scenery.isSculpture) {
+ this.mediaEditor.hide()
+ this.textEditor.hide()
+ this.sculptureEditor.pick(scenery)
+ }
+ else if (scenery.type == "text") {
+ this.mediaEditor.hide()
+ this.sculptureEditor.hide()
+ this.textEditor.pick(scenery)
+ }
+ else {
+ this.textEditor.hide()
+ this.sculptureEditor.hide()
+ this.mediaEditor.pick(scenery)
+ }
+ },
+
+ pickWall: function(wall, pos){
+ this.hideExtras()
+ this.wallpaperPicker.pickWall(wall)
},
hideExtras: function(){
+ this.sculptureEditor.hide()
this.mediaEditor.hide()
- }
+ this.textEditor.hide()
+ this.share.hide()
+ Sculpture.resize.hide()
+ Scenery.resize.hide()
+ Scenery.hovering = false
+ }
})
diff --git a/public/assets/javascripts/ui/editor/HelpCursor.js b/public/assets/javascripts/ui/editor/HelpCursor.js
new file mode 100644
index 0000000..4c8ff0c
--- /dev/null
+++ b/public/assets/javascripts/ui/editor/HelpCursor.js
@@ -0,0 +1,79 @@
+
+var HelpCursor = View.extend({
+ el: "#helpCursor",
+
+ active: false,
+
+ messages: {
+ start: "Welcome to VValls! Click one of the tools at right to learn about it.",
+ media: "This is where you pick media to go on the walls. You can upload media and paste links.",
+ addmedia: "Great, now click a wall to place this image.",
+ resize: "Drag the image to position it, or use the dots to resize.",
+ presets: "These are some basic presets to get you started. Click em! :-)",
+ wallpaper: "Click the wallpaper you want then apply it to the walls. Feel free to upload your own too!",
+ colors: "Use these colors to change the color of the walls, floor, and ceiling.",
+ settings: "This is where you publish your project. Give it a name, hit save, and you'll have a URL you can share with your friends.",
+ builder: "This is a map of your rooms. Draw new boxes, or move and resize the ones that are there. Hit ESCAPE to toggle the map.",
+ },
+
+ initialize: function(){
+ this.helpButton = $('#help-button')
+
+ this.helpButton.click(this.toggle.bind(this))
+ this.$el.html(this.messages['start'])
+ },
+
+ toggle: function(){
+ this.active ? this.stop() : this.start()
+ },
+
+ start: function(){
+ if (this.active) return
+ this.active = true
+ this.helpButton.addClass('active')
+ this.$el.show()
+ this.move({ pageX: -1000, pageY: -10000 })
+ this.moveFn = this.move.bind(this)
+ document.addEventListener("mousemove", this.moveFn)
+ },
+
+ stop: function(){
+ this.active = false
+ this.$el.hide()
+ this.helpButton.removeClass('active')
+ document.removeEventListener("mousemove", this.moveFn)
+ },
+
+ offset: 100,
+ lastPosition: { pageX: 0, pageY: 0 },
+ move: function(e){
+ this.el.style.right = clamp(window.innerWidth - e.pageX, this.offset, window.innerWidth) + "px"
+ this.el.style.top = e.pageY + "px"
+ this.lastPosition = e
+ },
+
+ show: function(name){
+ if (this.active) {
+ this.$el.show()
+ }
+ },
+ hide: function(){
+ this.$el.hide()
+ },
+
+ message: function(name){
+ if (! this.active) return
+ if (name == "start" || name == "media" || name == "settings") {
+ this.offset = 100
+ }
+ else if (name == "colors") {
+ this.offset = 270
+ }
+ else {
+ this.offset = 290
+ }
+ this.move(this.lastPosition)
+ this.$el.html(this.messages[name])
+ },
+
+})
diff --git a/public/assets/javascripts/ui/editor/MediaEditor.js b/public/assets/javascripts/ui/editor/MediaEditor.js
index 9b20a43..6068c48 100644
--- a/public/assets/javascripts/ui/editor/MediaEditor.js
+++ b/public/assets/javascripts/ui/editor/MediaEditor.js
@@ -3,7 +3,7 @@ var MediaEditor = FormView.extend({
el: "#mediaEditor",
events: {
- "keydown": 'stopPropagation',
+ "keydown": 'taint',
"focus [name]": "clearMinotaur",
"click [data-role=play-media]": "togglePaused",
"mousedown [name=keyframe]": "stopPropagation",
@@ -51,40 +51,56 @@ var MediaEditor = FormView.extend({
},
pick: function(scenery) {
- if (this.scenery) {
+ if (this.scenery && scenery !== this.scenery) {
this.unbind()
}
this.bind(scenery)
this.$el.addClass("active")
+// app.controller.toolbar.resetMode()
+ app.controller.toolbar.resetControls()
+ Scenery.resize.show(scenery)
+ Scenery.hovering = true
+
var media = scenery.media
- this.$name.val(media.title)
+ // console.log(media)
+
+ this.$name.val(media.title) // || filenameFromUrl(media.url) )
this.$description.val(media.description)
this.setDimensions()
this.$units.val( "ft" )
switch (media.type) {
case "image":
- this.$(".image").show()
this.$(".video").hide()
-
+ this.$(".audio").hide()
+ this.$(".image").show()
break
case "youtube":
case "vimeo":
case "video":
- this.$(".video").show()
this.$(".image").hide()
+ this.$(".audio").hide()
+ this.$(".video").show()
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
+
+ case "soundcloud":
+ this.$(".image").hide()
+ this.$(".video").hide()
+ this.$(".audio").show()
+ this.$playButton.toggleClass("paused", ! this.scenery.paused())
+ this.$autoplay.prop('checked', !! media.autoplay)
+ this.$loop.prop('checked', !! media.loop)
+ break
}
},
@@ -98,12 +114,14 @@ var MediaEditor = FormView.extend({
seek: function(){
var n = parseFloat( this.$keyframe.val() )
this.scenery.seek(n)
+ this.tainted = true
this.scenery.media.keyframe = n
},
setAutoplay: function(){
var checked = this.$autoplay.prop('checked')
this.scenery.media.autoplay = checked
+ this.tainted = true
if (checked && this.scenery.paused()) {
this.togglePaused()
}
@@ -111,17 +129,20 @@ var MediaEditor = FormView.extend({
setLoop: function(){
var checked = this.$loop.prop('checked')
this.scenery.setLoop(checked)
+ this.tainted = true
},
setMute: function(){
var checked = this.$mute.prop('checked')
this.scenery.media.mute = checked
this.scenery.mute(checked)
+ this.tainted = true
},
setDimensions: function(){
if (! this.scenery) return
this.$width.unitVal( Number(this.scenery.naturalDimensions.a * this.scenery.scale) || "" )
this.$height.unitVal( Number(this.scenery.naturalDimensions.b * this.scenery.scale) || "" )
+ this.tainted = true
},
changeWidth: function(e){
e.stopPropagation()
@@ -137,6 +158,11 @@ var MediaEditor = FormView.extend({
app.units = this.$units.val()
this.$('.units').resetUnitVal()
},
+
+ taint: function(e){
+ e.stopPropagation()
+ this.tainted = true
+ },
bind: function(scenery){
this.scenery = scenery
@@ -146,24 +172,29 @@ var MediaEditor = FormView.extend({
unbind: function(){
if (this.scenery) {
- this.scenery.media.title = this.$name.val()
- this.scenery.media.description = this.$description.val()
- Minotaur.watch( app.router.editorView.settings )
-
+ this.scenery.focused = false
+ if (this.tainted && this.scenery.media) {
+ this.scenery.media.title = this.$name.val()
+ this.scenery.media.description = this.$description.val()
+ Minotaur.watch( app.router.editorView.settings )
+ }
if (this.scenery.mx) {
this.scenery.mx.bound = false
this.scenery.mx.el.classList.remove("picked")
}
}
+ this.tainted = false
this.scenery = null
},
destroy: function(){
- ConfirmModal.confirm("Are you sure you want delete to this media?", function(){
- var scenery = this.scenery
- this.hide()
- Scenery.remove(scenery.id)
- }.bind(this))
+ var scenery = this.scenery
+ this.hide()
+
+ scenery.remove()
+
+ this.tainted = false
+ this.scenery = null
},
})
diff --git a/public/assets/javascripts/ui/editor/MediaTumblr.js b/public/assets/javascripts/ui/editor/MediaTumblr.js
new file mode 100644
index 0000000..47419ae
--- /dev/null
+++ b/public/assets/javascripts/ui/editor/MediaTumblr.js
@@ -0,0 +1,53 @@
+
+var MediaTumblr = ModalView.extend({
+ el: "#tumblrUpload",
+
+ events: {
+ 'mousedown': "stopPropagation",
+ "keydown .url": "enterSubmit",
+ "click .exampleTumblr": "loadExample",
+ },
+
+ initialize: function(opt){
+ this.__super__.initialize.call(this)
+ this.parent = opt.parent
+ this.$url = this.$(".url")
+ },
+
+ show: function(){
+ this.$el.addClass("active")
+ this.$url.val("")
+ },
+
+ hide: function(){
+ this.$el.removeClass("active")
+ },
+
+ enterSubmit: function(e){
+ e.stopPropagation()
+ if (e.keyCode == 13) {
+ e.preventDefault()
+ var url = this.$tumblrUrl.val()
+ this.loadTumblr(url)
+ }
+ },
+
+ loadTumblr: function(url){
+ Parser.tumblr(url, function(media_list){
+ console.log(media_list)
+ this.parent.mediaViewer.$foundMediaContainer.empty()
+ media_list.reverse().forEach(function(media){
+ this.parent.mediaViewer.add(media, this.parent.mediaViewer.$foundMediaContainer)
+ }.bind(this))
+ }.bind(this))
+ },
+
+ loadExample: function(e){
+ e.preventDefault()
+ var name = $(e.currentTarget).html()
+ var url = "http://" + name + ".tumblr.com/"
+ this.$url.val(url)
+ this.loadTumblr(url)
+ },
+
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/ui/editor/MediaUpload.js b/public/assets/javascripts/ui/editor/MediaUpload.js
index 92cf2bd..d09e38c 100644
--- a/public/assets/javascripts/ui/editor/MediaUpload.js
+++ b/public/assets/javascripts/ui/editor/MediaUpload.js
@@ -1,11 +1,12 @@
var MediaUpload = UploadView.extend({
- el: ".fileUpload",
+ el: "#fileUpload",
createAction: "/api/media/new",
uploadAction: "/api/media/upload",
events: {
+ 'mousedown': "stopPropagation",
"keydown .url": "enterSubmit",
},
@@ -25,6 +26,7 @@ var MediaUpload = UploadView.extend({
},
enterSubmit: function(e){
+ e.stopPropagation()
if (e.keyCode == 13) {
e.preventDefault()
this.parse()
@@ -34,7 +36,10 @@ var MediaUpload = UploadView.extend({
parse: function(){
var url = this.$url.val()
this.$url.val("")
-
+ this.parseUrl(url)
+ },
+
+ parseUrl: function(url){
Parser.parse(url, function(media){
if (! media) {
alert("Not a valid image/video link")
@@ -42,7 +47,6 @@ var MediaUpload = UploadView.extend({
}
media._csrf = $("[name=_csrf]").val()
- console.log(media)
var request = $.ajax({
type: "post",
@@ -52,10 +56,15 @@ var MediaUpload = UploadView.extend({
request.done(this.add.bind(this))
}.bind(this))
},
-
+
add: function(media){
console.log(media)
- this.parent.mediaViewer.add(media)
+ this.parent.mediaViewer.addUploadedMedia(media)
+ },
+
+ error: function(error){
+ console.log(error)
+ alert(error.errors.media.message)
},
beforeUpload: function(){
diff --git a/public/assets/javascripts/ui/editor/MediaViewer.js b/public/assets/javascripts/ui/editor/MediaViewer.js
index 436c0cb..2ae5104 100644
--- a/public/assets/javascripts/ui/editor/MediaViewer.js
+++ b/public/assets/javascripts/ui/editor/MediaViewer.js
@@ -3,44 +3,82 @@ var MediaViewer = ModalView.extend({
el: ".mediaDrawer.mediaViewer",
destroyAction: "/api/media/destroy",
usesFileUpload: true,
-
+ loaded: false,
+ perPage: 12,
+ offset: 0,
+ fixedClose: true,
+
events: {
+ 'mousedown': "stopPropagation",
'click .foundToggle': "foundToggle",
'click .userToggle': "userToggle",
'click #deleteMedia': "deleteArmed",
+ 'click #randomize': "randomize",
'click .mediaContainer': "pick",
+ 'click .viewMore': "load",
+ 'keydown #tumblr-url': 'enterTumblrUrl',
},
initialize: function(opt){
this.__super__.initialize.call(this)
this.parent = opt.parent
+
+ this.$myMedia = this.$(".myMedia").addClass('active')
+ this.$myMediaContainer = this.$(".myMedia > .container")
+ this.$userToggle = this.$(".userToggle")
+
this.$foundMedia = this.$(".foundMedia")
- this.$myMedia = this.$(".myMedia")
+ this.$foundMediaContainer = this.$(".foundMedia > .container")
this.$foundToggle = this.$(".foundToggle")
- this.$userToggle = this.$(".userToggle")
+
+ this.$wallpaperMedia = this.$(".wallpaperMedia")
+ this.$wallpaperMediaContainer = this.$(".wallpaperMedia > .container")
+ this.$wallpaperToggle = this.$(".wallpaperToggle")
+
this.$deleteMedia = this.$("#deleteMedia")
- },
+ this.$viewMore = this.$(".viewMore")
+ this.$noMedia = this.$(".noMedia")
+ },
+ mode: "user",
+ wallpaperToggle: function(){
+ this.mode = "wallpaper"
+ this.$wallpaperMedia.addClass("active")
+ this.$foundMedia.addClass("inactive")
+ this.$myMedia.addClass("inactive").removeClass('active')
+ this.$("a").removeClass("active")
+ this.$foundToggle.addClass("active")
+ },
+
foundToggle: function(){
- this.foundMedia.addClass("active");
- this.myMedia.addClass("inactive");
- this.$("a").removeClass("active");
- this.foundToggle.addClass("active");
+ this.mode = "found"
+ this.$wallpaperMedia.removeClass("active")
+ this.$foundMedia.addClass("active")
+ this.$myMedia.addClass("inactive").removeClass('active')
+ this.$("a").removeClass("active")
+ this.$foundToggle.addClass("active")
+ this.parent.mediaUpload.hide()
+ this.parent.mediaTumblr.show()
},
userToggle: function(){
- this.foundMedia.removeClass("active");
- this.myMedia.removeClass("inactive");
- this.$("a").removeClass("active");
- this.userToggle.addClass("active");
+ this.mode = "user"
+ this.$wallpaperMedia.removeClass("active")
+ this.$foundMedia.removeClass("active")
+ this.$myMedia.removeClass("inactive").addClass('active')
+ this.$("a").removeClass("active")
+ this.$userToggle.addClass("active")
+ this.parent.mediaUpload.show()
+ this.parent.mediaTumblr.hide()
},
show: function(){
if (! this.loaded) {
this.load()
+ // this.loadTrending()
}
else {
- this.__super__.show.call(this)
+ this.reallyShow()
}
},
@@ -48,24 +86,110 @@ var MediaViewer = ModalView.extend({
this.__super__.hide.call(this)
this.deleteArmed(false)
this.parent.mediaUpload.hide()
+ this.parent.mediaTumblr.hide()
+ this.parent.cursor.message('start')
+ },
+
+ reallyShow: function(){
+ this.__super__.show.call(this)
+ if (this.mode == "user") {
+ this.userToggle()
+ }
+ else {
+ this.foundToggle()
+ }
+ this.parent.cursor.message("media")
},
load: function(){
- $.get("/api/media/user", this.populate.bind(this))
+ $.get("/api/media/user", { offset: this.offset, limit: this.perPage }, this.populate.bind(this))
+ },
+
+/*
+ loadTrending: function(){
+ var trending_imagery = [
+ 'https://d1ycxz9plii3tb.cloudfront.net/post_images/52ec0e20c9dc24f1d8000067/large.jpg',
+ 'https://d1ycxz9plii3tb.cloudfront.net/additional_images/4e6bf67bc23f490001004579/1/tall.jpg',
+ 'https://d1ycxz9plii3tb.cloudfront.net/additional_images/52dcca28139b2135030002a8/tall.jpg',
+ 'https://d1ycxz9plii3tb.cloudfront.net/additional_images/52927bb2b202a3669d000704/larger.jpg',
+ 'https://d1ycxz9plii3tb.cloudfront.net/additional_images/4f9f3a3ce262e60001000fb3/large.jpg',
+ 'http://2.bp.blogspot.com/-GD6IxUvsdOo/UdrcMFLVYNI/AAAAAAAAF2E/kbRfxMxiUlQ/s1600/okeeffe.jpg',
+ 'http://www.bobkessel.com/wordpress/wp-content/uploads/2009/10/moma-bob-kessel-410.jpg',
+ 'http://static1.artsy.net/partner_show_images/52f28f348b3b81f2fc000364/large.jpg',
+ 'http://static3.artsy.net/partner_show_images/52e83674c9dc24397f0000d8/large.jpg',
+ 'http://static0.artsy.net/partner_show_images/52d96d484b84801ef0000273/large.jpg',
+ 'http://static1.artsy.net/partner_show_images/52778616275b24f95c00011d/1/large.jpg',
+ 'http://static1.artsy.net/partner_show_images/52dc65311a1e86be6b000205/large.jpg',
+ ]
+ trending_imagery.forEach(function(url){
+ var loaded = false
+ var img = new Image ()
+ img.onload = function(){
+ if (loaded) return
+ loaded = true
+ var media = {
+ type: 'image',
+ url: url,
+ width: img.naturalWidth,
+ height: img.naturalHeight,
+ }
+ this.add(media, this.$foundMediaContainer)
+ }.bind(this)
+ img.src = url
+ if (img.complete && ! loaded) { img.onload() }
+ }.bind(this))
},
+*/
+
+ randomize: function(){
+ var $divs = this.$(".active .container").find(".mediaContainer").toArray()
+ if ($divs.length < 3) {
+ $divs = $divs.concat( this.$foundMediaContainer.find(".mediaContainer").toArray() )
+ }
+ var media_objs = $divs.map(function(el){
+ return $(el).data("media")
+ })
+ Scenery.randomize.add( media_objs )
+ this.hide()
+ },
populate: function(data){
- this.loaded = true
+ var scrollTop = this.loaded ? $('.myMedia .container').height() : 0
if (data && data.length) {
- data.forEach(this.add.bind(this))
+ if (data.length < this.perPage) {
+ this.$viewMore.hide()
+ }
+ data.forEach(function(media){
+ this.add(media, this.$myMediaContainer)
+ this.offset += 1
+ }.bind(this))
+ this.$noMedia.hide()
+ this.$deleteMedia.show()
}
else {
+ this.$viewMore.hide()
+ this.$noMedia.show()
this.$deleteMedia.hide()
}
- this.__super__.show.call(this)
+ if (this.loaded) {
+ this.$el.delay(300).animate({ scrollTop: scrollTop }, 200)
+ }
+ else {
+ this.loaded = true
+ this.reallyShow()
+ }
+ },
+
+ addUploadedMedia: function(media){
+ this.$deleteMedia.show()
+ this.$noMedia.hide()
+ this.add(media, this.$myMedia, true) // prepend
+ this.userToggle()
+ this.$el.scrollTop(0)
+ this.offset += 1
},
- add: function(media){
+ add: function(media, $container, prepend){
var image = new Image ()
var $span = $("<span>")
$span.addClass("mediaContainer")
@@ -87,13 +211,17 @@ var MediaViewer = ModalView.extend({
image.src = media.url
image.load()
break
+
+ case 'soundcloud':
+ image.src = media.thumbnail
+ break
}
$span.data("media", media)
$span.append(image)
- this.$(".myMedia").prepend($span)
- this.$deleteMedia.show()
+ if (prepend) $container.prepend($span)
+ else $container.append($span)
},
deleteIsArmed: false,
@@ -131,35 +259,23 @@ var MediaViewer = ModalView.extend({
if ($(".myMedia .mediaContainer").length == 0) {
this.$deleteMedia.hide()
+ this.$noMedia.show()
this.deleteArmed(false)
}
return
}
-// else {
-// this.picked = {}
-// this.picked.media = media
-// this.picked.image = image
-// }
-// },
-//
-// drag: function(e){
-// if (! this.pickedMedia) return
-// var media = this.picked.media
-// var image = this.picked.image
-// this.picked = null
-
this.hide()
-
+ this.parent.cursor.message('addmedia')
+
var $ants = $('.ants');
var $floatingImg = $('.floatingImg');
Scenery.nextMedia = media
-// console.log(media.type)
-
switch (media.type) {
case "video":
+ case "soundcloud":
$floatingImg.attr('src', '/assets/img/playbutton.png')
break
@@ -185,12 +301,15 @@ var MediaViewer = ModalView.extend({
$floatingImg.attr('src', '')
$(window).off('mousemove', _followCursor)
$(window).off('mousedown', _hideCursor)
+ app.off('cancel-scenery', _hideCursor)
$floatingImg.parent().removeClass('edit')
+ app.controller.cursor.message('resize')
}
$(window).on('mousemove', _followCursor)
$(window).on('mousedown', _hideCursor)
+ app.on('cancel-scenery', _hideCursor)
$ants.addClass('edit')
_followCursor(e)
},
-
+
})
diff --git a/public/assets/javascripts/ui/editor/Presets.js b/public/assets/javascripts/ui/editor/Presets.js
new file mode 100644
index 0000000..5f5ac35
--- /dev/null
+++ b/public/assets/javascripts/ui/editor/Presets.js
@@ -0,0 +1,122 @@
+var Presets = View.extend({
+ el: "#presets",
+
+ events: {
+ "mousedown": "stopPropagation",
+ "click .presets span": "selectPreset",
+ },
+
+ presets: {
+ wireframe: {
+ wall: [255,255,255],
+ outline: [0,0,0],
+ floor: [246,246,246],
+ ceiling: [255,255,255],
+ },
+ shaded: {
+ wall: [205,205,204],
+ outline: [0,0,0],
+ floor: [109,116,106],
+ ceiling: [159,163,157],
+ background: [109,116,106],
+ },
+ "P.Funk": {
+ wall: [255,63,78],
+ outline: [255,246,0],
+ floor: [255,255,0],
+ ceiling: [225,118,252],
+ },
+ inverse: {
+ wall: [0,0,0],
+ outline: [255,255,255],
+ floor: [0,0,0],
+ ceiling: [0,0,0],
+ },
+ matrix: {
+ wall: { src: "http://dumpfm.s3.amazonaws.com/images/20130225/1361818675427-dumpfm-melipone-matrixremixtransfast.gif", scale: 4.0, color: [0,0,0] },
+ outline: [0,0,0],
+ floor: [10,15,10],
+ ceiling: [0,0,0],
+ },
+ },
+
+ initialize: function(opt){
+ this.parent = opt.parent
+
+ this.$presets = this.$(".presets")
+ _.keys(this.presets).forEach(function(name){
+ var $swatch = $("<span>")
+ $swatch.html(capitalize(name))
+ $swatch.data('preset', name)
+ this.$presets.append($swatch)
+ }.bind(this))
+ },
+
+ modified: true,
+ lastPreset: "wireframe",
+
+ toggle: function(state){
+ this.$el.toggleClass("active", state)
+ this.parent.cursor.message(state ? "presets" : "start")
+ if (this.modified) {
+ this.$(".active").removeClass('active')
+ }
+ },
+
+ show: function(){
+ this.toggle(true)
+ },
+
+ hide: function(){
+ this.toggle(false)
+ },
+
+ selectPreset: function(e){
+ var preset = $(e.currentTarget).data('preset')
+ if (! this.presets[preset]) return
+ this.$(".active").removeClass('active')
+ $(e.currentTarget).addClass('active')
+ if (this.modified) {
+ UndoStack.push({
+ type: "choose-preset",
+ undo: { walls: Walls.serialize(), colors: Walls.copyColors(Walls.colors) },
+ redo: preset,
+ })
+ Minotaur.watch( app.router.editorView.settings )
+ }
+ else {
+ UndoStack.push({
+ type: "choose-another-preset",
+ undo: this.lastPreset,
+ redo: preset,
+ })
+ Minotaur.watch( app.router.editorView.settings )
+ }
+ this.lastPreset = preset
+ this.load(this.presets[preset])
+ this.modified = false
+ },
+
+ loadByName: function(name){
+ var preset = this.presets[name]
+ this.load(preset)
+ },
+ load: function(preset){
+ this.parent.colorControl.modes.forEach(function(mode){
+ var color
+ if (! preset[mode].length) {
+ Walls.setWallpaper[mode](preset[mode])
+ color = preset[mode].color
+ }
+ else {
+ Walls.clearWallpaper[mode]()
+ color = preset[mode]
+ }
+ Walls.setColor[mode](color)
+ this.parent.colorControl.$swatch[ mode ].css("background-color", rgb_string(color))
+ }.bind(this))
+ this.parent.colorControl.setMode(preset.wall.color ? "wall" : "floor")
+ Walls.setBodyColor()
+ },
+
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/ui/editor/SculptureEditor.js b/public/assets/javascripts/ui/editor/SculptureEditor.js
new file mode 100644
index 0000000..953260c
--- /dev/null
+++ b/public/assets/javascripts/ui/editor/SculptureEditor.js
@@ -0,0 +1,237 @@
+
+var SculptureEditor = FormView.extend({
+ el: "#sculptureEditor",
+
+ events: {
+ "keydown": 'taint',
+ "focus [name]": "clearMinotaur",
+ "click [data-role=play-media]": "togglePaused",
+ "mousedown [name=keyframe]": "stopPropagation",
+ "mousedown": "stopPropagation",
+ "change [name=keyframe]": "seek",
+ "change [name=autoplay]": "setAutoplay",
+ "change [name=billboard]": "setBillboard",
+ "change [name=outline]": "setOutline",
+ "change [name=outlineColor]": "setOutlineColor",
+ "change [name=loop]": "setLoop",
+ "change [name=mute]": "setMute",
+ "change [name=width]": 'changeWidth',
+ "change [name=height]": 'changeHeight',
+ "change [name=depth]": 'changeDepth',
+ "change [name=units]": 'changeUnits',
+ "click [data-role=destroy-sculpture]": "destroy",
+ },
+
+ initialize: function(opt){
+ this.parent = opt.parent
+ this.__super__.initialize.call(this)
+
+ this.$name = this.$("[name=name]")
+ this.$description = this.$("[name=description]")
+
+ this.$billboard = this.$("[name=billboard]")
+ this.$outline = this.$("[name=outline]")
+ this.$outlineColor = this.$("[name=outlineColor]")
+
+ // image fields
+ this.$width = this.$("[name=width]")
+ this.$height = this.$("[name=height]")
+ this.$depth = this.$("[name=depth]")
+ 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) {
+ if (state) {
+ this.parent.settings.toggle()
+ }
+ this.$el.toggleClass("active", state);
+ },
+
+ togglePaused: function(state){
+ var state = this.sculpture.toggle(state)
+ this.$playButton.toggleClass("paused", ! state)
+ },
+
+ pick: function(sculpture) {
+ if (this.sculpture && sculpture !== this.sculpture) {
+ this.unbind()
+ }
+
+ this.bind(sculpture)
+ this.$el.addClass("active")
+
+// app.controller.toolbar.resetMode()
+ app.controller.toolbar.resetControls()
+ Sculpture.resize.show(sculpture)
+ Sculpture.hovering = true
+
+ var media = sculpture.media
+
+ // console.log(media)
+ this.$name.val(media.title || "") // || filenameFromUrl(media.url) )
+ this.$description.val(media.description || "")
+ this.setDimensions()
+ this.$units.val( "ft" )
+
+ this.$outline.prop( 'checked', !! sculpture.outline )
+ this.$outlineColor.val( sculpture.outlineColor || "#000000" )
+ this.$billboard.prop( 'checked', !! sculpture.billboard )
+
+ switch (media.type) {
+ case "image":
+ this.$(".video").hide()
+ this.$(".audio").hide()
+ this.$(".image").show()
+ break
+
+ case "youtube":
+ case "vimeo":
+ case "video":
+ this.$(".image").hide()
+ this.$(".audio").hide()
+ this.$(".video").show()
+
+ this.$playButton.toggleClass("paused", ! this.sculpture.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
+
+ case "soundcloud":
+ this.$(".image").hide()
+ this.$(".video").hide()
+ this.$(".audio").show()
+ this.$playButton.toggleClass("paused", ! this.sculpture.paused())
+ this.$autoplay.prop('checked', !! media.autoplay)
+ this.$loop.prop('checked', !! media.loop)
+ break
+ }
+ },
+
+ hide: function(sculpture){
+ if (this.sculpture) {
+ this.unbind()
+ }
+ this.toggle(false)
+ },
+
+ seek: function(){
+ var n = parseFloat( this.$keyframe.val() )
+ this.sculpture.seek(n)
+ this.tainted = true
+
+ this.sculpture.media.keyframe = n
+ },
+ setAutoplay: function(){
+ var checked = this.$autoplay.prop('checked')
+ this.sculpture.media.autoplay = checked
+ this.tainted = true
+ if (checked && this.sculpture.paused()) {
+ this.togglePaused()
+ }
+ },
+ setLoop: function(){
+ var checked = this.$loop.prop('checked')
+ this.sculpture.setLoop(checked)
+ this.tainted = true
+ },
+ setMute: function(){
+ var checked = this.$mute.prop('checked')
+ this.sculpture.media.mute = checked
+ this.sculpture.mute(checked)
+ this.tainted = true
+ },
+
+ setBillboard: function(){
+ var checked = this.$billboard.prop('checked')
+ this.sculpture.setBillboard(checked)
+ this.tainted = true
+ },
+ setOutline: function(){
+ var checked = this.$outline.prop('checked')
+ this.sculpture.setOutline(checked)
+ this.tainted = true
+ },
+ setOutlineColor: function(){
+ var color = this.$outlineColor.val()
+ this.sculpture.setOutlineColor(color)
+ this.tainted = true
+ },
+
+ setDimensions: function(){
+ if (! this.sculpture) return
+ this.$width.unitVal( Number(this.sculpture.naturalDimensions.a * this.sculpture.scale) || "" )
+ this.$height.unitVal( Number(this.sculpture.naturalDimensions.b * this.sculpture.scale) || "" )
+ this.$depth.unitVal( Number(this.sculpture.naturalDimensions.c * this.sculpture.scale) || "" )
+ this.tainted = true
+ },
+ changeWidth: function(e){
+ e.stopPropagation()
+ this.sculpture.set_scale( this.$width.unitVal() / this.sculpture.naturalDimensions.a )
+ this.setDimensions()
+ this.sculpture.updateOutline()
+ },
+ changeHeight: function(e){
+ e.stopPropagation()
+ this.sculpture.set_scale( this.$height.unitVal() / this.sculpture.naturalDimensions.b )
+ this.setDimensions()
+ this.sculpture.updateOutline()
+ },
+ changeDepth: function(e){
+ e.stopPropagation()
+ this.sculpture.set_depth( this.$depth.unitVal() )
+ this.$depth.unitVal( Number(this.sculpture.naturalDimensions.c * this.sculpture.scale) || "" )
+ this.sculpture.updateOutline()
+ },
+ changeUnits: function(){
+ app.units = this.$units.val()
+ this.$('.units').resetUnitVal()
+ },
+
+ taint: function(e){
+ e.stopPropagation()
+ this.tainted = true
+ },
+
+ bind: function(sculpture){
+ this.sculpture = sculpture
+ this.sculpture.mx.bound = true
+ this.sculpture.mx.el.classList.add("picked")
+ },
+
+ unbind: function(){
+ if (this.sculpture) {
+ this.sculpture.focused = false
+ if (this.tainted && this.sculpture.media) {
+ this.sculpture.media.title = this.$name.val()
+ this.sculpture.media.description = this.$description.val()
+ Minotaur.watch( app.router.editorView.settings )
+ }
+ if (this.sculpture.mx) {
+ this.sculpture.mx.bound = false
+ this.sculpture.mx.el.classList.remove("picked")
+ }
+ }
+ this.tainted = false
+ this.sculpture = null
+ },
+
+ destroy: function(){
+ var sculpture = this.sculpture
+ this.hide()
+
+ sculpture.remove()
+
+ this.tainted = false
+ this.sculpture = null
+ },
+
+})
diff --git a/public/assets/javascripts/ui/editor/TextEditor.js b/public/assets/javascripts/ui/editor/TextEditor.js
new file mode 100644
index 0000000..53d5b9f
--- /dev/null
+++ b/public/assets/javascripts/ui/editor/TextEditor.js
@@ -0,0 +1,238 @@
+
+var TextEditor = FormView.extend({
+ el: "#textEditor",
+ tainted: false,
+ scenery: null,
+
+ events: {
+ "keydown": 'taint',
+ "focus [name]": "clearMinotaur",
+ "mousedown": "stopPropagation",
+ "change [name=font-family]": 'changeFontFamily',
+ "change [name=font-size]": 'changeFontSize',
+ "change [name=text-align]": 'changeTextAlign',
+ "click .swatch": 'showColorPicker',
+ "click [data-role=hide-color-picker]": 'hideColorPicker',
+ "click [data-role=hide-text-editor]": 'hide',
+ "input [name=text-body]": 'changeText',
+ "click [data-role=destroy-text]": "destroy",
+ "click .colors span": "setHue",
+ },
+
+ initialize: function(opt){
+ this.parent = opt.parent
+ this.__super__.initialize.call(this)
+
+ this.$textSettings = this.$(".text-setting")
+ this.$colorSettings = this.$(".color-setting")
+ this.$noTextMessage = this.$(".no-text")
+ this.$fontFamily = this.$("[name=font-family]")
+ this.$fontSize = this.$("[name=font-size]")
+ this.$textBody = this.$("[name=text-body]")
+ this.$textAlign = this.$("[name=text-align]")
+ this.$swatch = this.$(".swatch")
+
+ this.colorPicker = new LabColorPicker(this, 155, 155)
+ this.$(".color-picker").append( this.colorPicker.canvas )
+ this.$(".color-picker").append( this.colorPicker.cursor )
+ this.$(".slider").append( this.colorPicker.brightness )
+
+ this.$colors = this.$(".colors")
+ this.parent.colorControl.colors.forEach(function(color){
+ var $swatch = $("<span>")
+ $swatch.css("background-color","rgb(" + color + ")")
+ $swatch.data('color', color)
+ this.$colors.append($swatch)
+ }.bind(this))
+
+ this.$(".setting").hide()
+
+ app.on("cancel-scenery", function(){
+ this.createMode(true)
+ $("body").toggleClass("addText", false)
+ }.bind(this))
+
+ },
+
+ toggle: function(state){
+ this.$el.toggleClass("active", state)
+ if (state) {
+ $("#keyhint").fadeOut(200)
+ Scenery.nextMedia = {
+ type: 'text',
+ width: 600,
+ height: 450,
+ scale: 0.5,
+ font: { family: 'Lato', size: 24, align: 'left', color: "#000" },
+ }
+ this.createMode(true)
+ }
+ else {
+ $("[data-role='toggle-text-editor']").removeClass("inuse")
+ }
+ },
+
+ hide: function(scenery){
+ Scenery.nextMedia = null
+ if (this.scenery) {
+ this.unbind()
+ }
+ Scenery.resize.hide()
+ this.toggle(false)
+ },
+
+ taint: function(e){
+ e.stopPropagation()
+ this.tainted = true
+ },
+
+ bind: function(scenery){
+ this.tainted = false
+ this.scenery = scenery
+ this.scenery.mx.bound = true
+ this.scenery.mx.el.classList.add("picked")
+ this.scenery.media.font.color = this.scenery.media.font.color || [0,0,0]
+ },
+
+ unbind: function(){
+ if (this.scenery) {
+ this.scenery.focused = false
+ if (this.tainted) {
+ Minotaur.watch( app.router.editorView.settings )
+ }
+ if (this.scenery.mx) {
+ this.scenery.mx.bound = false
+ this.scenery.mx.el.classList.remove("picked")
+ }
+ if (! this.scenery.media || ! this.scenery.media.description || this.scenery.media.description == "") {
+ this.scenery.remove()
+ }
+ }
+ this.tainted = false
+ this.scenery = null
+ },
+
+ createMode: function(state){
+ this.hideColorPicker()
+ this.$textSettings.toggle(! state)
+ this.$noTextMessage.toggle(!! state)
+ $("body").toggleClass("addText", !! state)
+ },
+
+ pick: function(scenery){
+ if (this.scenery && scenery !== this.scenery) {
+ this.unbind()
+ }
+
+ this.parent.settings.hide()
+ Scenery.resize.show(scenery)
+ Scenery.hovering = true
+
+ this.bind(scenery)
+ this.$el.toggleClass("active", true)
+ this.$textBody.val( this.scenery.media.description )
+
+ this.$fontFamily.val( this.scenery.media.font.family )
+ this.$fontSize.val( this.scenery.media.font.size )
+ this.$textAlign.val( this.scenery.media.font.align )
+ this.setSwatchColor( this.scenery.media.font.color )
+
+ this.createMode(false)
+
+ if (! this.scenery.media.description) {
+ setTimeout(function(){
+ this.$textBody.focus()
+ }.bind(this), 100)
+ }
+ },
+
+ taint: function(e){
+ e.stopPropagation()
+ },
+
+ changeFontFamily: function(){
+ this.scenery.setFont({ family: this.$fontFamily.val() })
+ },
+
+ changeTextAlign: function(){
+ this.scenery.setFont({ align: this.$textAlign.val() })
+ },
+
+ changeFontSize: function(){
+ var size = parseInt( this.$fontSize.val() )
+ size && this.scenery.setFont({ size: size })
+ },
+
+ changeText: function(e){
+ e.stopPropagation()
+ var text = this.$textBody.val()
+ this.scenery.setText(text)
+ },
+
+ destroy: function(){
+ this.tainted = false
+ this.scenery.remove()
+ this.hide()
+ },
+
+ setSwatchColor: function(rgb){
+ this.$swatch.css("background-color", rgb_string(rgb))
+ },
+ showColorPicker: function(){
+ this.$textSettings.hide()
+ this.$colorSettings.show()
+
+ var color = this.scenery.media.font.color
+ this.labColor = this.colorPicker.load(color)
+ this.pickColor(color, this.labColor)
+
+ this.$el.addClass("color-mode")
+ },
+
+ hideColorPicker: function(e){
+ e && e.preventDefault()
+ this.$textSettings.show()
+ this.$colorSettings.hide()
+ this.$el.removeClass("color-mode")
+ },
+
+ pickColor: function(rgb, Lab){
+ this.labColor = Lab
+ this.setSwatchColor(rgb)
+ this.scenery.setFont({ color: rgb })
+ this.tainted = true
+ },
+
+ setHue: function(e){
+ var color = $(e.currentTarget).data('color')
+ this.labColor = this.colorPicker.load(color)
+ this.pickColor(color, this.labColor)
+ },
+
+ initialState: null,
+
+ begin: function(){
+ // this.initialState = this.serialize()
+ },
+
+ serialize: function(){
+ return {
+ rgb: Walls.colors[ this.mode ]
+ }
+ },
+
+ finalize: function(){
+ if (! this.initialState) { return }
+ UndoStack.push({
+ type: 'update-colors',
+ undo: this.initialState,
+ redo: this.serialize(),
+ })
+
+ this.initialState = null
+
+ // TODO: watch individual wall object here
+ Minotaur.watch( app.router.editorView.settings )
+ },
+
+})
diff --git a/public/assets/javascripts/ui/editor/WallpaperPicker.js b/public/assets/javascripts/ui/editor/WallpaperPicker.js
index 0dd2921..3640d6d 100644
--- a/public/assets/javascripts/ui/editor/WallpaperPicker.js
+++ b/public/assets/javascripts/ui/editor/WallpaperPicker.js
@@ -3,34 +3,57 @@ var WallpaperPicker = UploadView.extend({
el: ".wallpaper",
mediaTag: "wallpaper",
+ createAction: "/api/media/new",
uploadAction: "/api/media/upload",
+ destroyAction: "/api/media/destroy",
events: {
+ "contextmenu": 'contextmenu',
+ "mousedown": 'stopPropagation',
"click .swatch": 'pick',
"click .wallpaperRemove": 'remove',
+ "input [data-role='wallpaper-scale']": 'updateScale',
+ "change .url": "enterUrl",
+ "keydown .url": "enterSetUrl",
},
- initialize: function(){
+ initialize: function(opt){
+ this.parent = opt.parent
this.__super__.initialize.call(this)
this.$swatches = this.$(".swatches")
this.$remove = this.$(".wallpaperRemove")
- this.$remove.hide()
+
+ this.$url = this.$(".url")
+
+ this.$position = this.$("[data-role='wallpaper-position']")
+ this.$scale = this.$("[data-role='wallpaper-scale']")
+
+ this.$wallpaperResizeControls = this.$(".wallpaperResizeControls")
+ this.$wallpaperResizeControls.addClass('disabled')
+
+ this.initializePositionCursor()
},
loaded: false,
show: function(){
- if (! this.loaded) {
- this.load()
- }
- else {
- this.toggle(true)
- }
+ this.toggle(true)
},
-
hide: function(){
- this.__super__.hide.call(this)
+ this.toggle(false)
},
-
+
+ toggle: function (state) {
+ Scenery.nextWallpaper = null
+ app.tube('cancel-wallpaper')
+ this.$el.toggleClass("active", state)
+ if (state) {
+ this.parent.cursor.message("wallpaper")
+ if (! this.loaded) {
+ this.load()
+ }
+ }
+ },
+
load: function(){
$.get("/api/media/user", { tag: this.mediaTag }, this.populate.bind(this))
},
@@ -39,49 +62,98 @@ var WallpaperPicker = UploadView.extend({
this.loaded = true
if (data && data.length) {
data.forEach(this.add.bind(this))
+ this.$(".txt").hide()
+ }
+ else {
+ this.$(".txt").show()
}
this.toggle(true)
},
+ seenWallpapers: {},
add: function (media) {
if (media.type !== "image") { return }
+ if (this.seenWallpapers[ media.url ]) { return }
var swatch = document.createElement("div")
swatch.className = "swatch"
swatch.style.backgroundImage = "url(" + media.url + ")"
+ swatch.setAttribute("data-id", media._id)
this.$swatches.append(swatch)
this.$swatches.show()
- },
-
- toggle: function (state) {
- if (state && ! this.loaded) {
- this.show()
- }
- else {
- this.$el.toggleClass("active", state)
- }
- // toggle the class that makes the cursor a paintbucket
- // $("body").removeClass("pastePaper")
+ this.$(".txt").hide()
+ this.seenWallpapers[ media.url ] = true
},
- hide: function(){
- this.toggle(false)
+ addUrl: function (url){
+ Parser.loadImage(url, function(media){
+ if (! media) return
+ media._csrf = $("[name=_csrf]").val()
+ media.tag = this.mediaTag
+
+ var request = $.ajax({
+ type: "post",
+ url: this.createAction,
+ data: media,
+ })
+ request.done(this.add.bind(this))
+
+ }.bind(this))
},
-
+
beforeUpload: function(){
},
pick: function(e){
var $swatch = $(e.currentTarget)
- this.follow( e, $swatch.css('background-image') )
- this.$remove.show()
+ if (Scenery.nextWallpaper == "none") {
+ var _id = $swatch[0].getAttribute("data-id")
+ $swatch.remove()
+ this.destroy(_id, function(){})
+ }
+ else {
+ app.tube('cancel-wallpaper')
+ this.follow( e, $swatch.css('background-image') )
+ this.parent.presets.modified = true
+ }
},
remove: function(e){
- this.follow( e, "none" )
- $(".floatingSwatch").addClass("scissors")
+ if (Scenery.nextWallpaper) {
+ // remove red class to the wallpaper
+ Scenery.nextWallpaper = null
+ app.tube('cancel-wallpaper')
+ }
+ else {
+ // add red class to the wallpaper
+ this.follow( e, "none" )
+ $(".floatingSwatch").addClass("scissors")
+ this.$el.addClass("deleteArmed")
+ }
+ },
+
+ destroy: function(_id, cb){
+ $.ajax({
+ type: "delete",
+ url: this.destroyAction,
+ data: { _id: _id, _csrf: $("[name=_csrf]").val() }
+ }).complete(cb || function(){})
+ },
+
+ contextmenu: function(e){
+ if (Scenery.nextWallpaper) {
+ e.preventDefault()
+ this.cancel()
+ }
+ },
+ cancel: function(){
+ if (Scenery.nextWallpaper) {
+ Scenery.nextWallpaper = null
+ app.tube('cancel-wallpaper')
+ }
},
follow: function(e, wallpaper, icon){
+ var base = this
icon = icon || wallpaper
var $floatingSwatch = $(".floatingSwatch")
@@ -90,6 +162,8 @@ var WallpaperPicker = UploadView.extend({
Scenery.nextWallpaper = wallpaper
+ $(".floodMessage").show()
+
setTimeout(function(){
function _followCursor(e) {
$floatingSwatch.css({
@@ -97,14 +171,105 @@ var WallpaperPicker = UploadView.extend({
left: (e.pageX + 10) + 'px',
});
}
- $(window).on('mousemove', _followCursor)
- $(window).one('click', function () {
+ function _hideCursor (e) {
+ $(window).off('keydown', _floodRoom)
$(window).off('mousemove', _followCursor)
+ app.off('cancel-wallpaper', _hideCursor)
$floatingSwatch.removeClass("scissors").hide()
- });
+ $(".floodMessage").hide()
+ base.$el.removeClass("deleteArmed")
+ }
+ function _floodRoom (e) {
+ if (e.keyCode == 13) {
+ base.flood()
+ }
+ }
+ $(window).on('keydown', _floodRoom)
+ $(window).on('mousemove', _followCursor)
+ // $(window).one('click', _hideCursor);
+ app.on('cancel-wallpaper', _hideCursor)
$floatingSwatch.show()
_followCursor(e);
})
},
+ wall: null,
+ pickWall: function(wall){
+ if (! wall.background || wall.background.src == "none") {
+ this.$wallpaperResizeControls.addClass('disabled')
+ this.$scale.val( 0.0 )
+ return;
+ }
+ this.$wallpaperResizeControls.removeClass('disabled')
+ this.wall = wall
+ this.$scale.val( Math.log( this.wall.background.scale ) )
+ },
+
+ scaleTimeout: null,
+ updateScale: function(){
+ if (! this.wall) return;
+ var scale = Math.exp( parseFloat(this.$scale.val()) )
+ this.wall.wallpaperPosition({ scale: scale })
+
+ clearTimeout(this.scaleTimeout)
+ this.scaleTimeout = setTimeout(function(){
+
+ // TODO: watch individual scenery object here
+ Minotaur.watch( app.router.editorView.settings )
+
+ }.bind(this), 500)
+ },
+
+ enterUrl: function(){
+ var url = this.$url.sanitize()
+ this.addUrl(url)
+ this.$url.val("")
+ },
+ enterSetUrl: function (e) {
+ e.stopPropagation()
+ if (e.keyCode == 13) {
+ setTimeout(this.enterUrl.bind(this), 100)
+ }
+ },
+
+ flood: function(url){
+ url = url || Scenery.nextWallpaper
+ if (! url) return
+ Walls.setWallpaper.wall({ src: url })
+ Walls.setWallpaper.floor({ src: url })
+ Walls.setWallpaper.ceiling({ src: url })
+ this.cancel()
+ },
+
+ initializePositionCursor: function(){
+ var base = this
+ var dx = 0, dy = 0, dragging = false, delta
+ var x = 0, y = 0, s = 1
+ var mymouse = new mouse({
+ el: this.$position[0],
+ down: function(e, cursor){
+ if (! base.wall) return
+ dragging = true
+ // s = parseFloat( base.$scale.val() )
+ x = base.wall.background.x
+ y = base.wall.background.y
+ },
+ drag: function(e, cursor){
+ if (! dragging) return
+ delta = cursor.delta()
+ delta.a = - delta.a
+ dx = delta.a*s
+ dy = delta.b*s
+ base.wall.wallpaperPosition({
+ // scale: s,
+ x: x+dx,
+ y: y+dy,
+ })
+ },
+ up: function(e, cursor, new_cursor){
+ dragging = false
+ },
+ })
+ },
+
})
diff --git a/public/assets/javascripts/ui/lib/AlertModal.js b/public/assets/javascripts/ui/lib/AlertModal.js
index 1b0f40f..a4bf241 100644
--- a/public/assets/javascripts/ui/lib/AlertModal.js
+++ b/public/assets/javascripts/ui/lib/AlertModal.js
@@ -1,25 +1,25 @@
var AlertModal = new( ModalFormView.extend({
- el: ".mediaDrawer.alert",
+ el: ".mediaDrawer.alert",
- events: {
- "click .ok": "advance",
- "click .close": "advance",
- },
-
- alert: function(message, callback){
- this.$(".message").empty().append(message)
- this.callback = callback
- this.show()
- this.$(".ok").focus()
- },
-
- advance: function(e){
- e && e.preventDefault()
- this.hide()
- this.callback && this.callback()
- this.callback = null
- }
+ events: {
+ "click .ok": "advance",
+ "click .close": "advance",
+ },
+
+ alert: function(message, callback){
+ this.$(".message").empty().append(message)
+ this.callback = callback
+ this.show()
+ this.$(".ok").focus()
+ },
+
+ advance: function(e){
+ e && e.preventDefault()
+ this.hide()
+ this.callback && this.callback()
+ this.callback = null
+ }
}))
diff --git a/public/assets/javascripts/ui/lib/AnimatedView.js b/public/assets/javascripts/ui/lib/AnimatedView.js
new file mode 100644
index 0000000..3c50b0a
--- /dev/null
+++ b/public/assets/javascripts/ui/lib/AnimatedView.js
@@ -0,0 +1,31 @@
+var AnimatedView = View.extend({
+
+ _animating: false,
+ last_t: 0,
+
+ startAnimating: function(){
+ if (this._animating) return
+ this._animating = true
+ this._animate()
+ },
+
+ stopAnimating: function(){
+ this._animating = false
+ },
+
+ _animate: function(t){
+ if (! this._animating) return
+
+ requestAnimationFrame(this._animate.bind(this))
+
+ var dt = t - this.last_t
+ this.last_t = t
+
+ if (! t) return
+
+ this.animate(t, dt)
+ },
+
+ animate: function(t, dt){},
+
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/ui/lib/ConfirmModal.js b/public/assets/javascripts/ui/lib/ConfirmModal.js
index 01720bb..7d9da67 100644
--- a/public/assets/javascripts/ui/lib/ConfirmModal.js
+++ b/public/assets/javascripts/ui/lib/ConfirmModal.js
@@ -1,24 +1,34 @@
var ConfirmModal = new( ModalFormView.extend({
- el: ".mediaDrawer.confirm",
+ el: ".mediaDrawer.confirm",
- events: {
- "click .yes": "advance",
- "click .no": "hide",
- },
-
- confirm: function(question, callback){
- this.$(".question").empty().append(question)
- this.callback = callback
- this.show()
- },
-
- advance: function(e){
- e && e.preventDefault()
- this.hide()
- this.callback && this.callback()
- this.callback = null
- }
+ events: {
+ "click .yes": "agree",
+ "click .no": "cancel",
+ },
+
+ confirm: function(question, agreeCallback, cancelCallback){
+ this.$(".question").empty().append(question)
+ this.agreeCallback = agreeCallback
+ this.cancelCallback = cancelCallback
+ this.show()
+ },
+
+ agree: function(e){
+ e && e.preventDefault()
+ this.hide()
+ this.agreeCallback && this.agreeCallback()
+ this.agreeCallback = null
+ this.cancelCallback = null
+ },
+
+ cancel: function(e){
+ e && e.preventDefault()
+ this.hide()
+ this.cancelCallback && this.cancelCallback()
+ this.agreeCallback = null
+ this.cancelCallback = null
+ }
}) ) \ No newline at end of file
diff --git a/public/assets/javascripts/ui/lib/ErrorModal.js b/public/assets/javascripts/ui/lib/ErrorModal.js
index 8b01077..cfc2e6d 100644
--- a/public/assets/javascripts/ui/lib/ErrorModal.js
+++ b/public/assets/javascripts/ui/lib/ErrorModal.js
@@ -1,26 +1,26 @@
var ErrorModal = new( ModalFormView.extend({
- el: ".mediaDrawer.error",
+ el: ".mediaDrawer.error",
- events: {
- "click .ok": "advance",
- "click .close": "advance",
- },
-
- alert: function(message, callback){
- this.$(".errorList").empty().append(message)
- this.callback = callback
- this.show()
- this.$(".ok").focus()
- },
-
- advance: function(e){
- e && e.preventDefault()
- this.hide()
- this.callback && this.callback()
- this.callback = null
- }
+ events: {
+ "click .ok": "advance",
+ "click .close": "advance",
+ },
+
+ alert: function(message, callback){
+ this.$(".errorList").empty().append(message)
+ this.callback = callback
+ this.show()
+ this.$(".ok").focus()
+ },
+
+ advance: function(e){
+ e && e.preventDefault()
+ this.hide()
+ this.callback && this.callback()
+ this.callback = null
+ }
}))
diff --git a/public/assets/javascripts/ui/lib/FormView.js b/public/assets/javascripts/ui/lib/FormView.js
index 17b748a..a952ecb 100644
--- a/public/assets/javascripts/ui/lib/FormView.js
+++ b/public/assets/javascripts/ui/lib/FormView.js
@@ -1,127 +1,138 @@
var FormView = View.extend({
- method: "post",
+ method: "post",
+ useMinotaur: false,
- events: {
- "submit form": "save"
- },
+ events: {
+ "submit form": "save"
+ },
- initialize: function(opt){
- if (opt && opt.parent) {
- this.parent = opt.parent
- }
- this.$form = this.$("form")
- this.$errors = this.$(".errors")
- this.$errorList = this.$(".errorList")
- },
+ initialize: function(opt){
+ if (opt && opt.parent) {
+ this.parent = opt.parent
+ }
+ this.$form = this.$("form")
+ this.$errors = this.$(".errors")
+ this.$errorList = this.$(".errorList")
+ },
- reset: function(){
- this.$("input,textarea").not("[type='submit']").not("[type='hidden']").val("")
- },
-
- showErrors: function(errors){
- if (errors && errors.length) {
- this.$errorList.empty();
- for (var i in errors) {
- this.$errorList.append('<div>' + errors[i] + '</div>');
- }
- this.$errors.css("opacity", 1.0);
- setTimeout(function(){
- this.$errors.show().css("opacity", 1.0);
- }.bind(this), 200)
- }
- },
-
- serialize: function(){
- var fd = new FormData(), hasCSRF = false
-
- this.$("input[name], select[name], textarea[name]").each( function(){
- if (this.type == "file") {
- if (this.files.length > 0) {
- fd.append(this.name, this.files[0]);
- }
- }
- else if (this.type == "password") {
- if (this.value.length > 0) {
- fd.append(this.name, SHA1.hex('lol$' + this.value + '$vvalls'))
- }
- }
- else {
- fd.append(this.name, this.value);
- hasCSRF = hasCSRF || this.name == "_csrf"
- }
- });
-
- if (! hasCSRF) {
+ reset: function(){
+ this.$("input,textarea").not("[type='submit']").not("[type='hidden']").val("")
+ },
+
+ showErrors: function(errors){
+ if (errors && errors.length) {
+ this.$errorList.empty();
+ for (var i in errors) {
+ this.$errorList.append('<div>' + errors[i] + '</div>');
+ }
+ this.$errors.css("opacity", 1.0);
+ setTimeout(function(){
+ this.$errors.show().css("opacity", 1.0);
+ }.bind(this), 200)
+ }
+ },
+
+ serialize: function(){
+ var fd = new FormData(), hasCSRF = false
+
+ this.$("input[name], select[name], textarea[name]").each( function(){
+ if (this.type == "file") {
+ if (this.files.length > 0) {
+ fd.append(this.name, this.files[0]);
+ }
+ }
+ else if (this.type == "password") {
+ if (this.value.length > 0) {
+ fd.append(this.name, SHA1.hex('lol$' + this.value + '$vvalls'))
+ }
+ }
+ else {
+ fd.append(this.name, this.value);
+ hasCSRF = hasCSRF || this.name == "_csrf"
+ }
+ });
+
+ if (! hasCSRF) {
fd.append("_csrf", $("[name=_csrf]").val())
}
-
- return fd
- },
-
- save: function(e, successCallback, errorCallback){
- e && e.preventDefault()
+
+ return fd
+ },
+
+ save: function(e, successCallback, errorCallback){
+ e && e.preventDefault()
- this.$errors.hide().css("opacity", 0.0);
-
- if (this.validate) {
- var errors = this.validate()
- if (errors && errors.length) {
- if (errorCallback) {
- errorCallback(errors)
- }
- else {
+ this.$errors && this.$errors.hide().css("opacity", 0.0);
+
+ if (this.validate) {
+ var errors = this.validate()
+ if (errors && errors.length) {
+ if (errorCallback) {
+ setTimeout(function(){
+ errorCallback(errors)
+ })
+ }
+ else {
this.showErrors(errors)
- }
- return
- }
- }
-
- var action = typeof this.action == "function" ? this.action() : this.action
- if (! action) return
+ }
+ return
+ }
+ }
+ var action = typeof this.action == "function" ? this.action() : this.action
+ if (! action) return
- var request = $.ajax({
- url: action,
- type: this.method,
- data: this.serialize(),
- dataType: "json",
- processData: false,
- contentType: false,
- })
-
- request.done($.proxy(function (response) {
- if (response.error) {
- var errors = []
- for (var key in response.error.errors) {
- errors.push(response.error.errors[key].message);
- }
- if (errorCallback) {
- errorCallback(errors)
- }
- else {
+ var request = $.ajax({
+ url: action,
+ type: this.method,
+ data: this.serialize(),
+ dataType: "json",
+ processData: false,
+ contentType: false,
+ })
+
+ if (this.useMinotaur) {
+ Minotaur.show()
+ }
+
+ request.done($.proxy(function (response) {
+ if (this.useMinotaur) {
+ Minotaur.hide()
+ }
+ if (response.error) {
+ var errors = []
+ for (var key in response.error.errors) {
+ errors.push(response.error.errors[key].message);
+ }
+ if (errorCallback) {
+ errorCallback(errors)
+ }
+ else {
this.showErrors(errors)
- }
- return
- }
- else {
- if (successCallback) {
- successCallback(response)
- }
- if (this.success) {
- this.success(response)
- }
- }
- }, this));
- }
+ }
+ return
+ }
+ else {
+ console.log("ok")
+ if (successCallback) {
+ console.log("use cb")
+ successCallback(response)
+ }
+ if (this.success) {
+ this.success(response)
+ }
+ }
+ }, this));
+ }
})
var ModalFormView = ModalView.extend(FormView.prototype).extend({
- load: function(){
- this.reset()
- this.show()
- }
+ load: function(){
+ this.reset()
+ this.show()
+ }
})
diff --git a/public/assets/javascripts/ui/editor/LightControl.js b/public/assets/javascripts/ui/lib/LabColorPicker.js
index 3eb2861..2c8fb90 100644
--- a/public/assets/javascripts/ui/editor/LightControl.js
+++ b/public/assets/javascripts/ui/lib/LabColorPicker.js
@@ -1,145 +1,22 @@
-
-var LightControl = View.extend({
- el: ".lightcontrol",
-
- events: {
- "mousedown": "stopPropagation",
- "click .color-swatches span": "select",
- "input #shadow-control": "updateShadow",
- "mousedown #brightness-control": "beginBrightness",
- "input #brightness-control": "updateBrightness",
- "input #outline-hue": "updateShadow",
- "input #wall-hue": "updateShadow",
- },
-
- initialize: function(){
-
- this.colorPicker = new LabColorPicker(this, 180, 180)
- this.$("#color-picker").append( this.colorPicker.canvas )
- this.$("#color-picker").append( this.colorPicker.cursor )
-
- this.$swatches = this.$(".swatch")
- this.$labels = this.$(".swatch + label")
- this.$swatch = {
- wall: this.$("#wall-color"),
- outline: this.$("#outline-color"),
- floor: this.$("#floor-color"),
- ceiling: this.$("#ceiling-color"),
- }
- this.$brightnessControl = this.$("#brightness-control")
- },
-
- modes: [ "wall", "outline", "floor", "ceiling" ],
-
- load: function(data){
- this.modes.forEach(function(mode){
- Walls.setColor[mode](data[mode])
- this.$swatch[ mode ].css("background-color", rgb_string(data[mode]))
- }.bind(this))
- this.setMode("wall")
- },
-
- loadDefaults: function(){
- var colors = {
- wall: app.defaults.colors.wall.slice(),
- outline: app.defaults.colors.outline.slice(),
- floor: app.defaults.colors.floor.slice(),
- ceiling: app.defaults.colors.ceiling.slice(),
- }
- this.load(colors)
- },
-
- toggle: function(state){
- this.$el.toggleClass("active", state);
- },
-
- show: function(){
- this.toggle(true)
- },
-
- hide: function(){
- this.toggle(false)
- },
-
- pick: function(rgb, Lab){
- this.labColor = Lab
- this.setSwatchColor(this.mode, rgb)
- Walls.setColor[ this.mode ](rgb)
- },
-
- setSwatchColor: function(mode, rgb) {
- this.$swatch[ mode ].css("background-color", rgb_string(rgb))
- },
-
- initialState: null,
-
- begin: function(){
- this.initialState = this.serialize()
- },
-
- serialize: function(){
- return {
- mode: this.mode,
- rgb: Walls.colors[ this.mode ]
- }
- },
-
- finalize: function(){
- if (! this.initialState) { return }
- UndoStack.push({
- type: 'update-colors',
- undo: this.initialState,
- redo: this.serialize(),
- })
-
- this.initialState = null
-
- // TODO: watch individual wall object here
- Minotaur.watch( app.router.editorView.settings )
- },
-
- setMode: function (mode) {
- var color, brightness
- this.mode = mode
- this.$(".active").removeClass("active")
- this.$swatch[ mode ].parent().addClass("active")
- color = Walls.colors[ mode ]
-
- this.labColor = this.colorPicker.load(color)
- this.$brightnessControl.val( this.labColor[0] )
- },
-
- select: function(e){
- var mode = $('.swatch', e.currentTarget).data('mode')
- this.setMode(mode)
- },
-
- beginBrightness: function(){
- this.begin()
- $(window).one("mouseup", this.finalize.bind(this))
- },
-
- updateBrightness: function(){
- this.labColor[0] = parseFloat( this.$brightnessControl.val() )
- var rgb = this.colorPicker.setLab( this.labColor )
- this.pick(rgb, this.labColor)
- },
-
-})
-
var LabColorPicker = function (parent, w, h) {
var base = this
var canvas = this.canvas = document.createElement('canvas')
- var ctx = this.ctx = canvas.getContext('2d')
- var imageData = ctx.createImageData(w,h)
- var data = imageData.data
+ canvas.width = w
+ canvas.height = h
+ var ctx = this.ctx = canvas.getContext('2d-lodpi')
+// canvas.className = "colorPicker"
+// var imageData = ctx.createImageData(w, h)
+// var data = imageData.data
var cursor = this.cursor = document.createElement("div")
cursor.className = "colorPickerCursor"
-
- canvas.width = w
- canvas.height = h
- canvas.className = "colorPicker"
+
+ var brightnessControl = this.brightness = document.createElement("input")
+ var $brightnessControl = $(brightnessControl)
+ brightnessControl.setAttribute("type", "range")
+ brightnessControl.setAttribute("min", "0")
+ brightnessControl.setAttribute("max", "110")
+ brightnessControl.setAttribute("value", "0")
var ww = w-1
var hh = h-1
@@ -168,6 +45,11 @@ var LabColorPicker = function (parent, w, h) {
}
})
+ $brightnessControl.on({
+ "mousedown": function(){ base.beginBrightness() },
+ "input": function(){ base.updateBrightness() },
+ })
+
this.setLab = function(Lab) {
val = Lab[0]
this.paint()
@@ -181,7 +63,7 @@ var LabColorPicker = function (parent, w, h) {
var y = mix( j/hh, b_range[0], b_range[1] )
var rgb = xyz2rgb(hunterlab2xyz(val, x, y)).map(Math.round)
this.moveCursor(i, j)
- parent.pick( rgb, [val,x,y] )
+ parent.pickColor( rgb, [val,x,y] )
}
this.load = function(rgba){
var Lab = xyz2hunterlab(rgb2xyz(rgba))
@@ -191,6 +73,8 @@ var LabColorPicker = function (parent, w, h) {
this.moveCursor(x,y)
this.setLab(Lab)
+ this.setBrightness(Lab)
+
return Lab
}
this.moveCursor = function(x,y){
@@ -199,11 +83,14 @@ var LabColorPicker = function (parent, w, h) {
}
this.paint = function() {
val = clamp(val, L_range[0], L_range[1])
- var x, y, t
- for (var i = 0; i < w; i++) {
- for (var j = 0; j < h; j++) {
- x = mix( i/ww, a_range[0], a_range[1] )
- y = mix( j/hh, b_range[0], b_range[1] )
+ var imageData = ctx.createImageData(canvas.width, canvas.height)
+ var data = imageData.data
+ var x, y, t, cw = imageData.width, ch = imageData.height
+ var cww = cw-1, chh = ch-1
+ for (var i = 0; i < cw; i++) {
+ for (var j = 0; j < ch; j++) {
+ x = mix( i/cww, a_range[0], a_range[1] )
+ y = mix( j/chh, b_range[0], b_range[1] )
t = (j*w + i) * 4
rgb = xyz2rgb(hunterlab2xyz(val, x, y))
data[t] = Math.round( rgb[0] )
@@ -214,6 +101,19 @@ var LabColorPicker = function (parent, w, h) {
}
ctx.putImageData(imageData,0,0)
}
+
+ this.beginBrightness = function(){
+ parent.begin()
+ $(window).one("mouseup", parent.finalize.bind(parent))
+ }
+ this.updateBrightness = function(){
+ parent.labColor[0] = parseFloat( $brightnessControl.val() )
+ var rgb = base.setLab( parent.labColor )
+ parent.pickColor(rgb, parent.labColor)
+ }
+ this.setBrightness = function(Lab){
+ $brightnessControl.val(Lab[0])
+ }
function hunterlab2xyz (L,a,b) {
var_Y = L / 10
diff --git a/public/assets/javascripts/ui/lib/ModalView.js b/public/assets/javascripts/ui/lib/ModalView.js
index d9b518a..e0070ce 100644
--- a/public/assets/javascripts/ui/lib/ModalView.js
+++ b/public/assets/javascripts/ui/lib/ModalView.js
@@ -1,42 +1,53 @@
var ModalView = View.extend({
- events: {
- "click .close": 'close',
- },
-
- initialize: function(opt){
- if (opt && opt.parent) {
- this.parent = opt.parent
- }
- },
-
- usesFileUpload: false,
-
- show: function(){
- $(".mediaDrawer").removeClass("active")
-
- if (! this.usesFileUpload) {
- $(".fileUpload").removeClass("active")
- }
-
- this.$el.addClass("active")
+ events: {
+ "click .close": 'close',
+ },
+
+ initialize: function(opt){
+ if (opt && opt.parent) {
+ this.parent = opt.parent
+ }
+ },
+
+ usesFileUpload: false,
+
+ show: function(){
+ $(".mediaDrawer").removeClass("active")
+ $(".fileUpload").removeClass("active")
+
+ if (this.fixedClose) {
+ $("#fixed_close").addClass("active")
+ $("#fixed_close").bind("click", this.hide.bind(this))
+ }
+
+ this.$el.addClass("active")
$("body").addClass("noOverflow")
- },
+ },
- hide: function(){
+ hide: function(){
// $(".mediaDrawer, .room1").removeClass("active editing");
- this.$el.removeClass("active");
+ if (this.fixedClose) {
+ $("#fixed_close").removeClass("active")
+ $("#fixed_close").unbind("click", this.hide.bind(this))
+ }
+ this.$el.removeClass("active");
$("body").removeClass("noOverflow");
- },
-
- close: function(){
- if (window.isModalView) {
- window.location.pathname = "/"
- }
- else {
- history.pushState(null, document.title, app.router.originalPath)
- this.hide()
- }
- }
+ },
+
+ hideClose: function(){
+ $("#fixed_close").removeClass("active")
+ $("#fixed_close").unbind("click", this.hide.bind(this))
+ },
+
+ close: function(){
+ if (window.isModalView) {
+ window.location.pathname = "/"
+ }
+ else {
+ history.pushState(null, document.title, app.router.originalPath)
+ this.hide()
+ }
+ }
})
diff --git a/public/assets/javascripts/ui/lib/Parser.js b/public/assets/javascripts/ui/lib/Parser.js
index 52c96e6..411f425 100644
--- a/public/assets/javascripts/ui/lib/Parser.js
+++ b/public/assets/javascripts/ui/lib/Parser.js
@@ -5,9 +5,12 @@ var Parser = {
fetch: function(url, done) {
var img = new Image ()
img.onload = function(){
+ if (!img) return
var width = img.naturalWidth, height = img.naturalHeight
img = null
done({
+ url: url,
+ type: "image",
token: "",
thumbnail: "",
title: "",
@@ -32,6 +35,8 @@ var Parser = {
var width = video.videoWidth, height = video.videoHeight
video = null
done({
+ url: url,
+ type: "video",
token: "",
thumbnail: "",
title: "",
@@ -63,6 +68,8 @@ var Parser = {
success: function(result){
var res = result.items[0]
done({
+ url: url,
+ type: "youtube",
token: id,
thumbnail: thumb,
title: res.snippet.title,
@@ -92,6 +99,8 @@ var Parser = {
return
}
done({
+ url: url,
+ type: "vimeo",
token: id,
thumbnail: res.thumbnail_large,
title: res.title,
@@ -103,10 +112,9 @@ var Parser = {
},
tag: function (media) {
// return '<img class="video" type="vimeo" vid="'+media.token+'" src="'+media.thumbnail+'"><span class="playvid">&#9654;</span>';
- return '<div class="video" style="width: ' + media.width + 'px; height: ' + media.height + 'px; overflow: hidden; position: relative;"><iframe frameborder="0" scrolling="no" seamless="seamless" webkitallowfullscreen="webkitAllowFullScreen" mozallowfullscreen="mozallowfullscreen" allowfullscreen="allowfullscreen" id="okplayer" src="http://player.vimeo.com/video/' + media.token + '?api=1&js_api=1&title=0&byline=0&portrait=0&playbar=0&player_id=okplayer&loop=0&autoplay=0" width="' + media.width + '" height="' + media.height + '" style="position: absolute; top: 0px; left: 0px; width: ' + media.width + 'px; height: ' + media.height + 'px;"></iframe></div>'
+ return '<div class="video" style="width: ' + media.width + 'px; height: ' + media.height + 'px; overflow: hidden; position: relative;"><iframe frameborder="0" scrolling="no" seamless="seamless" webkitallowfullscreen="webkitAllowFullScreen" mozallowfullscreen="mozallowfullscreen" allowfullscreen="allowfullscreen" id="okplayer" src="http://player.vimeo.com/video/' + media.token + '?api=1&title=0&byline=0&portrait=0&playbar=0&player_id=okplayer&loop=0&autoplay=0" width="' + media.width + '" height="' + media.height + '" style="position: absolute; top: 0px; left: 0px; width: ' + media.width + 'px; height: ' + media.height + 'px;"></iframe></div>'
}
},
- /*
{
type: 'soundcloud',
regex: /soundcloud.com\/[-a-zA-Z0-9]+\/[-a-zA-Z0-9]+\/?$/i,
@@ -118,26 +126,33 @@ var Parser = {
+ '&client_id='
+ '0673fbe6fc794a7750f680747e863b10',
success: function(result) {
+ // console.log(result)
done({
+ url: url,
+ type: "soundcloud",
token: result.id,
- thumbnail: "",
- title: "",
- width: 100,
- height: 100,
+ thumbnail: result.artwork_url || result.user.avatar_url,
+ title: result.user.username + " - " + result.title,
+ width: 166,
+ height: 166,
})
}
});
},
tag: function (media) {
- return '<iframe width="400" height="166" scrolling="no" frameborder="no"' +
+ return '<iframe width="166" height="166" scrolling="no" frameborder="no"' +
'src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/' + media.token +
'&amp;color=ff6600&amp;auto_play=false&amp;show_artwork=true"></iframe>'
}
- }, {
+ },
+ /*
+ {
type: 'link',
regex: /^http.+/i,
fetch: function(url, done) {
done({
+ url: url,
+ type: "link",
token: "",
thumbnail: "",
title: "",
@@ -152,12 +167,86 @@ var Parser = {
*/
],
+ tumblr: function(url, cb){
+ var domain = url.replace(/^https?:\/\//,"").split("/")[0]
+ if (domain.indexOf(".") == -1) {
+ domain += ".tumblr.com"
+ }
+ $.ajax({
+ type: 'GET',
+ url: "http://" + domain + "/api/read",
+ dataType: "jsonp",
+ data: {
+ format: "json",
+ },
+ success: function(data){
+ var media_list = []
+ var blog = data.tumblelog
+
+ data.posts.forEach(parse)
+ cb(media_list)
+
+ function parse(post){
+ var media, caption, url
+ switch (post.type) {
+ case 'photo':
+ caption = stripHTML(post['photo-caption'])
+ if (post.photos.length) {
+ post.photos.forEach(function(photo){
+ var media = {
+ url: photo['photo-url-1280'],
+ type: "image",
+ token: "",
+ thumbnail: photo['photo-url-500'],
+ description: caption,
+ width: parseInt(photo.width),
+ height: parseInt(photo.height),
+ }
+ media_list.push(media)
+ })
+ }
+ else {
+ media = {
+ url: post['photo-url-1280'],
+ type: "image",
+ token: "",
+ thumbnail: post['photo-url-500'],
+ description: caption,
+ width: parseInt(post.width),
+ height: parseInt(post.height),
+ }
+ media_list.push(media)
+ }
+ break
+ case 'video':
+ url = post['video-source']
+ if (url.indexOf("http") !== 0) { break }
+ if (Parser.lookup.youtube.regex.test(url)) {
+ 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"
+ media = {
+ url: post['video-source'],
+ type: "youtube",
+ token: id,
+ thumbnail: thumb,
+ title: stripHTML(post['video-caption']),
+ width: 640,
+ height: 360,
+ }
+ media_list.push(media)
+ }
+ break
+ }
+ }
+// console.log(post)
+ }
+ })
+ },
+
parse: function (url, cb) {
var matched = Parser.integrations.some(function(integration){
if (integration.regex.test(url)) {
integration.fetch(url, function(res){
- res.url = url
- res.type = integration.type
cb(res)
})
return true
@@ -176,6 +265,15 @@ var Parser = {
return ""
},
+ loadImage: function(url, cb, error){
+ if (Parser.lookup.image.regex.test(url)) {
+ Parser.lookup.image.fetch(url, function(media){
+ cb(media)
+ })
+ }
+ else error && error()
+ },
+
thumbnail: function (media) {
return '<img src="' + (media.thumbnail || media.url) + '" class="thumb">';
},
diff --git a/public/assets/javascripts/ui/lib/Router.js b/public/assets/javascripts/ui/lib/Router.js
index 0b6385c..28905b2 100644
--- a/public/assets/javascripts/ui/lib/Router.js
+++ b/public/assets/javascripts/ui/lib/Router.js
@@ -16,6 +16,7 @@ var Router = View.extend({
if (pathname in routes) {
this[this.routes[pathname]](null)
+ return
}
if (path[path.length-1] == null) {
diff --git a/public/assets/javascripts/ui/lib/ToggleableView.js b/public/assets/javascripts/ui/lib/ToggleableView.js
new file mode 100644
index 0000000..371629f
--- /dev/null
+++ b/public/assets/javascripts/ui/lib/ToggleableView.js
@@ -0,0 +1,19 @@
+var ToggleableView = View.extend({
+
+ toggle: function(state){
+ this.$el.toggleClass("active", state)
+ },
+
+ show: function(){
+ this.toggle(true)
+ },
+
+ hide: function(){
+ this.toggle(false)
+ },
+
+ visible: function(){
+ return this.$el.hasClass("active")
+ }
+
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/ui/lib/Toolbar.js b/public/assets/javascripts/ui/lib/Toolbar.js
new file mode 100644
index 0000000..a9ce51c
--- /dev/null
+++ b/public/assets/javascripts/ui/lib/Toolbar.js
@@ -0,0 +1,22 @@
+var Toolbar = Fiber.extend(function(base){
+ var exports = {}
+ exports.init = function(rapper){
+ this.rapper = (typeof rapper == "string") ? $(rapper)[0] : rapper
+ this.tools = {}
+ this.els = {}
+ }
+ exports.add = function(role, fn){
+ var self = this
+ this.tools[role] = fn
+ this.els[role] = $("[data-role=" + role + "]", self.rapper)
+ this.els[role].click(function(){
+ $(".active", self.rapper).removeClass('active')
+ $(this).addClass('active')
+ fn()
+ })
+ }
+ exports.pick = function(role){
+ this.els[role].trigger("click")
+ }
+ return exports
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/ui/lib/UploadView.js b/public/assets/javascripts/ui/lib/UploadView.js
index efaa8c9..d1e2b73 100644
--- a/public/assets/javascripts/ui/lib/UploadView.js
+++ b/public/assets/javascripts/ui/lib/UploadView.js
@@ -3,88 +3,94 @@ var UploadView = View.extend({
// define uploadAction
- events: {
- "change .file": "handleFileSelect",
- "submit form": "preventDefault",
- },
-
- initialize: function(){
- this.$file = this.$(".file")
- this.$upload = this.$(".upload-icon")
+ events: {
+ "change [type=file]": "handleFileSelect",
+ "submit form": "preventDefault",
+ },
+
+ initialize: function(){
+ this.$file = this.$("[type=file]")
+ this.$upload = this.$(".upload-icon")
},
beforeUpload: function(){
},
-
+
handleFileSelect: function(e) {
- e.stopPropagation();
- e.preventDefault();
-
- this.beforeUpload()
+ e.stopPropagation();
+ e.preventDefault();
+
+ this.beforeUpload()
- var files = e.dataTransfer ? e.dataTransfer.files : e.target.files;
+ var files = e.dataTransfer ? e.dataTransfer.files : e.target.files;
- for (var i = 0, f; f = files[i]; i++) {
- if ( ! f.type.match('image.*')) {
- continue;
- }
-
- this.getImageDimensions(f)
- }
- },
-
- getImageDimensions: function(f){
- var base = this
-
- this.$upload.addClass('uploading')
+ for (var i = 0, f; f = files[i]; i++) {
+ if ( ! f.type.match('image.*')) {
+ continue;
+ }
+
+ this.getImageDimensions(f)
+ }
+ },
+
+ getImageDimensions: function(f){
+ var base = this
+
+ this.$upload.addClass('uploading')
- var reader = new FileReader();
+ var reader = new FileReader();
- reader.onload = function(e) {
- var image = new Image()
- image.onload = function(){
- var width = image.naturalWidth,
- height = image.naturalHeight
- base.upload(f, width, height)
- }
- image.src = e.target.result
- }
-
- reader.readAsDataURL(f);
- },
-
- upload: function(f, width, height){
- var fd = new FormData()
- fd.append('image', f)
- fd.append('width', width)
- fd.append('height', height)
- fd.append('_csrf', $("[name=_csrf]").val())
-
- if (this.mediaTag) {
- fd.append('tag', this.mediaTag)
- }
+ reader.onload = function(e) {
+ var image = new Image()
+ image.onload = function(){
+ var width = image.naturalWidth,
+ height = image.naturalHeight
+ base.upload(f, width, height)
+ }
+ image.src = e.target.result
+ }
+
+ reader.readAsDataURL(f);
+ },
+
+ upload: function(f, width, height){
+ var fd = new FormData()
+ fd.append('image', f)
+ fd.append('width', width)
+ fd.append('height', height)
+ fd.append('_csrf', $("[name=_csrf]").val())
+
+ if (this.mediaTag) {
+ fd.append('tag', this.mediaTag)
+ }
- var request = $.ajax({
- url: this.uploadAction,
- type: "post",
- data: fd,
- dataType: "json",
- processData: false,
- contentType: false,
- })
- request.done(this.success.bind(this))
- },
-
- success: function(media){
- if (media.error) {
- return
- }
- this.$upload.removeClass('uploading')
- this.add(media)
- },
-
- add: function(media){
- console.log(media)
- },
+ var request = $.ajax({
+ url: this.uploadAction,
+ type: "post",
+ data: fd,
+ dataType: "json",
+ processData: false,
+ contentType: false,
+ })
+ request.done(this.success.bind(this))
+ },
+
+ success: function(media){
+ if (media.error) {
+ // console.log(media.error)
+ this.$upload.removeClass('uploading')
+ this.error(media.error)
+ return
+ }
+ this.$upload.removeClass('uploading')
+ this.add(media)
+ },
+
+ add: function(media){
+ console.log(media)
+ },
+
+ error: function(error){
+ },
})
diff --git a/public/assets/javascripts/ui/lib/View.js b/public/assets/javascripts/ui/lib/View.js
index d94e6db..9a8ab5b 100644
--- a/public/assets/javascripts/ui/lib/View.js
+++ b/public/assets/javascripts/ui/lib/View.js
@@ -1,139 +1,142 @@
var View = (function($, _){
- var View = function(options) {
- this._id = _.uniqueId('view')
- this.type = "view"
- options || (options = {});
- _.extend(this, _.pick(options, viewOptions))
- this._ensureElement()
- this.initialize.apply(this, arguments)
- this.delegateEvents()
- }
+ var View = function(options) {
+ this._id = _.uniqueId('view')
+ this.type = "view"
+ options || (options = {});
+ _.extend(this, _.pick(options, viewOptions))
+ this._ensureElement()
+ this.initialize.apply(this, arguments)
+ this.delegateEvents()
+ }
- var delegateEventSplitter = /^(\S+)\s*(.*)$/;
+ var delegateEventSplitter = /^(\S+)\s*(.*)$/;
- var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
+ var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
- _.extend(View.prototype, {
+ _.extend(View.prototype, {
- // The default `tagName` of a View's element is `"div"`.
- tagName: 'div',
+ // The default `tagName` of a View's element is `"div"`.
+ tagName: 'div',
- $: function(selector) {
- return this.$el.find(selector);
- },
+ $: function(selector) {
+ return this.$el.find(selector);
+ },
- initialize: function(){},
+ initialize: function(){},
- setElement: function(element, delegate) {
- if (this.$el) this.undelegateEvents();
- this.$el = element instanceof $ ? element : $(element);
- this.el = this.$el[0];
- if (delegate !== false) this.delegateEvents();
- return this;
- },
+ setElement: function(element, delegate) {
+ if (this.$el) this.undelegateEvents();
+ this.$el = element instanceof $ ? element : $(element);
+ this.el = this.$el[0];
+ if (delegate !== false) this.delegateEvents();
+ return this;
+ },
- // Set callbacks, where `this.events` is a hash of
- //
- // *{"event selector": "callback"}*
- //
- // {
- // 'mousedown .title': 'edit',
- // 'click .button': 'save',
- // 'click .open': function(e) { ... }
- // }
- //
- // pairs. Callbacks will be bound to the view, with `this` set properly.
- // Uses event delegation for efficiency.
- // Omitting the selector binds the event to `this.el`.
- // This only works for delegate-able events: not `focus`, `blur`, and
- // not `change`, `submit`, and `reset` in Internet Explorer.
- delegateEvents: function(events) {
- if (!(events || (events = _.result(this, 'events')))) return this;
- this.undelegateEvents();
- for (var key in events) {
- var method = events[key];
- if (!_.isFunction(method)) method = this[events[key]];
- if (!method) continue;
+ // Set callbacks, where `this.events` is a hash of
+ //
+ // *{"event selector": "callback"}*
+ //
+ // {
+ // 'mousedown .title': 'edit',
+ // 'click .button': 'save',
+ // 'click .open': function(e) { ... }
+ // }
+ //
+ // pairs. Callbacks will be bound to the view, with `this` set properly.
+ // Uses event delegation for efficiency.
+ // Omitting the selector binds the event to `this.el`.
+ // This only works for delegate-able events: not `focus`, `blur`, and
+ // not `change`, `submit`, and `reset` in Internet Explorer.
+ delegateEvents: function(events) {
+ if (!(events || (events = _.result(this, 'events')))) return this;
+ this.undelegateEvents();
+ for (var key in events) {
+ var method = events[key];
+ if (!_.isFunction(method)) method = this[events[key]];
+ if (!method) continue;
- var match = key.match(delegateEventSplitter);
- var eventName = match[1], selector = match[2];
- method = _.bind(method, this);
- eventName += '.delegateEvents' + this._id;
- if (selector === '') {
- this.$el.on(eventName, method);
- } else {
- this.$el.on(eventName, selector, method);
- }
- }
- return this;
- },
+ var match = key.match(delegateEventSplitter);
+ var eventName = match[1], selector = match[2];
+ method = _.bind(method, this);
+ eventName += '.delegateEvents' + this._id;
+ if (is_mobile && (selector === 'mouseenter' || selector === 'mouseleave')) {
+ continue
+ }
+ else if (selector === '') {
+ this.$el.on(eventName, method);
+ } else {
+ this.$el.on(eventName, selector, method);
+ }
+ }
+ return this;
+ },
- // Clears all callbacks previously bound to the view with `delegateEvents`.
- undelegateEvents: function() {
- this.$el.off('.delegateEvents' + this._id);
- return this;
- },
+ // Clears all callbacks previously bound to the view with `delegateEvents`.
+ undelegateEvents: function() {
+ this.$el.off('.delegateEvents' + this._id);
+ return this;
+ },
- // Ensure that the View has a DOM element to render into.
- // If `this.el` is a string, pass it through `$()`, take the first
- // matching element, and re-assign it to `el`. Otherwise, create
- // an element from the `id`, `className` and `tagName` properties.
- _ensureElement: function() {
- this.setElement(_.result(this, 'el'), false);
- },
-
- preventDefault: function(e){
- e && e.preventDefault()
- },
-
- stopPropagation: function(e){
- e && e.stopPropagation()
- },
+ // Ensure that the View has a DOM element to render into.
+ // If `this.el` is a string, pass it through `$()`, take the first
+ // matching element, and re-assign it to `el`. Otherwise, create
+ // an element from the `id`, `className` and `tagName` properties.
+ _ensureElement: function() {
+ this.setElement(_.result(this, 'el'), false);
+ },
+
+ preventDefault: function(e){
+ e && e.preventDefault()
+ },
+
+ stopPropagation: function(e){
+ e && e.stopPropagation()
+ },
- });
+ });
- var extend = function(protoProps, staticProps) {
- var staticProps = staticProps || {}
- var parent = this;
- var child;
- var childEvents = {};
+ var extend = function(protoProps, staticProps) {
+ var staticProps = staticProps || {}
+ var parent = this;
+ var child;
+ var childEvents = {};
- // The constructor function for the new subclass is either defined by you
- // (the "constructor" property in your `extend` definition), or defaulted
- // by us to simply call the parent's constructor.
- if (protoProps && _.has(protoProps, 'constructor')) {
- child = protoProps.constructor;
- } else {
- child = function(){ return parent.apply(this, arguments); };
- }
+ // The constructor function for the new subclass is either defined by you
+ // (the "constructor" property in your `extend` definition), or defaulted
+ // by us to simply call the parent's constructor.
+ if (protoProps && _.has(protoProps, 'constructor')) {
+ child = protoProps.constructor;
+ } else {
+ child = function(){ return parent.apply(this, arguments); };
+ }
- // Extend events so we can subclass views
- _.extend(childEvents, parent.prototype.events, protoProps.events)
+ // Extend events so we can subclass views
+ _.extend(childEvents, parent.prototype.events, protoProps.events)
- // Add static properties to the constructor function, if supplied.
- _.extend(child, parent, staticProps);
+ // Add static properties to the constructor function, if supplied.
+ _.extend(child, parent, staticProps);
- // Set the prototype chain to inherit from `parent`, without calling
- // `parent`'s constructor function.
- var Surrogate = function(){ this.constructor = child; };
- Surrogate.prototype = parent.prototype;
- child.prototype = new Surrogate;
+ // Set the prototype chain to inherit from `parent`, without calling
+ // `parent`'s constructor function.
+ var Surrogate = function(){ this.constructor = child; };
+ Surrogate.prototype = parent.prototype;
+ child.prototype = new Surrogate;
- // Add prototype properties (instance properties) to the subclass,
- // if supplied.
- if (protoProps) _.extend(child.prototype, protoProps);
+ // Add prototype properties (instance properties) to the subclass,
+ // if supplied.
+ if (protoProps) _.extend(child.prototype, protoProps);
- // Set a convenience property in case the parent's prototype is needed
- // later.
- child.prototype.__super__ = parent.prototype;
- child.prototype.events = childEvents
+ // Set a convenience property in case the parent's prototype is needed
+ // later.
+ child.prototype.__super__ = parent.prototype;
+ child.prototype.events = childEvents
- return child;
- };
+ return child;
+ };
- View.extend = extend;
-
- return View;
+ View.extend = extend;
+
+ return View;
})(jQuery, _)
diff --git a/public/assets/javascripts/ui/reader/EmbedView.js b/public/assets/javascripts/ui/reader/EmbedView.js
new file mode 100644
index 0000000..7a75e00
--- /dev/null
+++ b/public/assets/javascripts/ui/reader/EmbedView.js
@@ -0,0 +1,76 @@
+var EmbedView = ModalView.extend({
+ el: ".embedView",
+
+ events: {
+ "keydown": "stopPropagation",
+ "input [name=width]": "build",
+ "input [name=height]": "build",
+ "click [name=mute]": "build",
+ "click [name=interactive]": "build",
+ "click textarea": "selectAll",
+ "click #testEmbed": "test",
+ },
+
+ defaultWidth: 600,
+ defaultHeight: 450,
+
+ initialize: function(opt){
+ this.parent = opt.parent
+ this.$embedCode = this.$("#embedCode")
+ this.$width = this.$("[name=width]")
+ this.$height = this.$("[name=height]")
+ this.$mute = this.$("[name=mute]")
+ this.$interactive = this.$("[name=interactive]")
+
+ this.$width.val(this.defaultWidth)
+ this.$height.val(this.defaultHeight)
+ },
+
+ show: function(){
+ this.build()
+ this.__super__.show.call(this)
+ },
+
+ build: function(){
+ var kode = this.getEmbedCode()
+ this.$embedCode.val( kode )
+ },
+
+ getEmbedCode: function(){
+ var mute = this.$mute.prop('checked') ? 1 : 0
+ var interactive = this.$interactive.prop('checked') ? 1 : 0
+ var width = clamp( this.$width.int(), 0, 2000) || this.defaultWidth
+ var height = clamp( this.$height.int(), 0, 2000) || this.defaultHeight
+ var link = this.parent.getLink()
+ var embed_link = link
+ embed_link += "?mute=" + mute
+ embed_link += "&embed=1"
+ if (interactive) {
+ embed_link += "&interactive=1"
+ }
+
+ var kode = "<iframe src='" + encodeURI(embed_link) + "' width='" + width + "' height='" + height + "'"
+ kode += " seamless scrolling='no' style='border: 0'"
+ kode += " webkitallowfullscreen mozallowfullscreen allowfullscreen"
+ if (! interactive) {
+ kode += " style='pointer-events:none;'"
+ }
+ kode += "></iframe>"
+
+ if (! interactive) {
+ kode = "<div style='position:relative'>" + kode + "<a href='" + encodeURI(link) + "' style='display:block;position:absolute;top:0;left:0;width:" + width + "px;height:" + height + "px;'></a></div>"
+ }
+
+ return kode
+ },
+
+ test: function(){
+ var kode = this.getEmbedCode()
+ window.open("data:text/html," + kode, "_blank")
+ },
+
+ selectAll: function(){
+ this.$embedCode[0].select()
+ },
+
+})
diff --git a/public/assets/javascripts/ui/reader/MediaPlayer.js b/public/assets/javascripts/ui/reader/MediaPlayer.js
index df2d075..8e65976 100644
--- a/public/assets/javascripts/ui/reader/MediaPlayer.js
+++ b/public/assets/javascripts/ui/reader/MediaPlayer.js
@@ -1,5 +1,5 @@
-var MediaPlayer = FormView.extend({
+var MediaPlayer = View.extend({
el: "#mediaPlayer",
events: {
@@ -44,22 +44,26 @@ var MediaPlayer = FormView.extend({
this.unbind()
}
if (media.type == "image") {
- if ((! media.title || ! media.title.length) && (! media.description || ! media.description.length)) {
+ if ( ! media.description && (! media.title || media.title == filenameFromUrl(media.url)) ) {
this.hide()
- return
+ return false
}
}
+ else if (media.type == "text") {
+ return false
+ }
this.bind(scenery)
this.$el.addClass("active")
- this.$name.html(media.title)
- this.$description.html(media.description)
+ this.$name.html( sanitize(media.title) )
+ this.$description.html( marked(media.description) )
switch (media.type) {
case "image":
- this.$(".image").show()
this.$(".video").hide()
+ this.$(".audio").hide()
+ this.$(".image").show()
// this.$widthDimension.html( Number(media.widthDimension) || "" )
// this.$heightDimension.html( Number(media.heightDimension) || "" )
@@ -70,14 +74,24 @@ var MediaPlayer = FormView.extend({
case "youtube":
case "vimeo":
case "video":
- this.$(".video").show()
this.$(".image").hide()
+ this.$(".audio").hide()
+ this.$(".video").show()
this.$playButton.toggleClass("paused", ! this.scenery.paused())
this.$muteButton.toggleClass("muted", this.scenery.muted())
break
+
+ case "soundcloud":
+ this.$(".image").hide()
+ this.$(".video").hide()
+ this.$(".audio").show()
+
+ this.$playButton.toggleClass("paused", ! this.scenery.paused())
+ break
}
+ return true
},
hide: function(scenery){
diff --git a/public/assets/javascripts/ui/reader/ReaderView.js b/public/assets/javascripts/ui/reader/ReaderView.js
index d80f225..43e81d8 100644
--- a/public/assets/javascripts/ui/reader/ReaderView.js
+++ b/public/assets/javascripts/ui/reader/ReaderView.js
@@ -9,36 +9,98 @@ var ReaderView = View.extend({
initialize: function(){
this.mediaPlayer = new MediaPlayer ({ parent: this })
+ this.shareView = new ShareView ({ parent: this })
},
load: function(name){
- if (window.location.search.indexOf("noui") !== -1) {
- $(".logo,.topLinks,#editorView").hide()
+ var opt = this.getQS()
+ var mode = "default"
+ var name = sanitize(name)
+
+ if (opt.noui) {
+ $(".logo, .topLinks, #editorView, #keyhint").hide()
+ mode = "noui"
+ }
+ if (opt.embed) {
+ $(".topLinks, .share, #edit-room-link, #keyhint").hide()
+ mode = "embed"
}
- if (window.location.search.indexOf("mute") !== -1) {
+ if (opt.mute) {
app.muted = true
}
- name = sanitize(name)
- $.get(this.projectAction + name, this.ready.bind(this))
+
+ this.tracker = new Tracker ({ mode: mode })
+
+ if ('vvalls_data' in window) {
+ this.ready(window.vvalls_data)
+ }
+ else {
+ $.get(this.projectAction + name, this.ready.bind(this))
+ }
+ },
+
+ getQS: function(){
+ var qs = {}
+ window.location.search.replace(/^\?/,"").split("&").forEach(function(s){
+ var pair = s.split("=")
+ if (pair.length < 2) {
+ qs[pair[0]] = true
+ }
+ else {
+ qs[pair[0]] = pair[1]
+ }
+ })
+ return qs
},
ready: function(data){
$("#map").hide()
+ this.data = data
+ var is_landscape = window.innerWidth > window.innerHeight
- data.rooms && Rooms.deserialize(data.rooms)
+ if (is_desktop || (is_mobile && is_landscape)) {
+ this.build(data)
+ return
+ }
+
+ // don't build anything until we're in landscape mode, otherwise ios might crash!!
+ var orientationFn = orientationchange.bind(this)
+ window.addEventListener('orientationchange', orientationFn)
+ function orientationchange (e) {
+ var is_landscape = window.innerWidth > window.innerHeight
+ if (is_landscape) {
+ window.removeEventListener('orientationchange', orientationFn)
+ this.build(data)
+ }
+ }
+ },
+
+ build: function(data){
+ if (data.shapes.length) {
+ Rooms.deserializeFromShapes(data)
+ }
+ else {
+ Rooms.deserialize(data.rooms)
+ }
data.walls && Walls.deserialize(data.walls)
data.media && Scenery.deserialize(data.media)
+ data.sculpture && Sculpture.deserialize(data.sculpture)
data.startPosition && scene.camera.move(data.startPosition)
+ cam.y = window.viewHeight = data.viewHeight || app.defaults.viewHeight
+
var colors = data.colors || app.defaults.colors
var modes = [ "wall", "outline", "floor", "ceiling" ]
modes.forEach(function(mode){
Walls.setColor[mode](colors[mode])
})
- editor.permissions.clear()
+ window.editor && editor.permissions.clear()
- this.listen()
+ // disable until we start using spinning again
+ // this.listen()
+
+ app.tube("site-ready")
},
listen: function(){
@@ -71,11 +133,19 @@ var ReaderView = View.extend({
},
pick: function(scenery){
- this.mediaPlayer.pick(scenery)
+ var has_info = this.mediaPlayer.pick(scenery)
+ $("#minimap").toggleClass('active', ! has_info)
+ app.tube("pick-scenery", { scenery: scenery })
+ },
+
+ pickWall: function(wall, pos){
+ this.hideExtras()
},
hideExtras: function(){
+ $("#minimap").addClass('active')
this.mediaPlayer.hide()
+ app.tube("close-scenery")
}
})
diff --git a/public/assets/javascripts/ui/reader/ShareView.js b/public/assets/javascripts/ui/reader/ShareView.js
new file mode 100644
index 0000000..dbe6f64
--- /dev/null
+++ b/public/assets/javascripts/ui/reader/ShareView.js
@@ -0,0 +1,62 @@
+var ShareView = View.extend({
+ el: ".share",
+
+ events: {
+ "keydown": "stopPropagation",
+ "click #share_facebook": "facebook",
+ "click #share_twitter": "twitter",
+ "click #share_embed": "embed",
+ },
+
+ initialize: function(opt){
+ this.parent = opt.parent
+ this.embedView = new EmbedView ({ parent: this })
+ this.$link = this.$("#share_link")
+ },
+
+ toggle: function(state){
+ if (typeof state == "boolean") {
+ this.$el.toggleClass("active", state)
+ }
+ else {
+ this.$el.toggleClass("active")
+ }
+ },
+ show: function(){
+ this.toggle(true)
+ },
+ hide: function(){
+ this.toggle(false)
+ },
+
+ setLink: function(url){
+ this.$link.val( url )
+ },
+ getLink: function(){
+ var link = window.location.origin + window.location.pathname
+ link = link.replace(/\/edit\/?$/, "")
+ return link
+ },
+
+ facebook: function (e) {
+ e.preventDefault()
+ var link = this.getLink()
+ var msg = app.controller.data.name + " on VValls"
+ var url = "https://www.facebook.com/share.php?u=" + encodeURIComponent(link) + "&t=" + encodeURIComponent(msg)
+ window.open(url, "_blank")
+ },
+
+ twitter: function (e) {
+ e.preventDefault()
+ var link = this.getLink()
+ var msg = app.controller.data.name + " on VValls"
+ var url = "https://twitter.com/home?status=" + encodeURIComponent(link + " " + msg)
+ window.open(url, "_blank")
+ },
+
+ embed: function (e) {
+ e.preventDefault()
+ this.embedView.show()
+ },
+
+})
diff --git a/public/assets/javascripts/ui/reader/Tracker.js b/public/assets/javascripts/ui/reader/Tracker.js
new file mode 100644
index 0000000..d2dec39
--- /dev/null
+++ b/public/assets/javascripts/ui/reader/Tracker.js
@@ -0,0 +1,133 @@
+if (window.location.host.indexOf("lvh.me") === -1) {
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+}
+else {
+ ga = function(){}
+}
+ga('create', 'UA-56883705-1', 'auto');
+ga('send', 'pageview');
+
+var Tracker = Fiber.extend(function(base){
+
+ var exports = {
+ init: function(opt){
+ this.wall_id = null
+ this.scenery_id = null
+ this.clicks = 0
+
+ this.wallTimer = new Timer ()
+ this.roomTimer = new Timer ()
+ this.sceneryTimer = new Timer ()
+
+ this.bind()
+ // this.trackPageview(opt)
+ },
+
+ bind: function () {
+ window.addEventListener("click", this.trackClick.bind(this), true)
+ app.on("change-wall", this.changeWall.bind(this))
+ app.on("pick-scenery", this.pickScenery.bind(this))
+ app.on("close-scenery", this.trackScenery.bind(this))
+ app.on("change-room", this.changeRoom.bind(this))
+ },
+
+ pushEvent: function(event){
+ // this.events.push(event)
+ event.unshift("send")
+ ga.apply( ga, event )
+ },
+
+ trackPageview: function(opt){
+ // this.pushEvent([ "view", opt.mode ])
+ },
+
+ //
+ // how long they spend in front of each wall
+
+ changeWall: function(opt){
+ var duration = this.wallTimer.currentTime()
+ if (this.wall_id && duration > 5000) {
+ this.pushEvent([ "wall", this.wall_id, duration ])
+ }
+ this.wall_id = opt.wall.id
+ this.wallTimer.start()
+ },
+
+ //
+ // how long the user spends on each item they click
+
+ pickScenery: function(opt){
+ if (this.scenery_id && opt.scenery.id !== this.scenery_id) {
+ this.trackScenery()
+ }
+ else {
+ this.sceneryTimer.start()
+ }
+ this.scenery_id = opt.scenery.id
+ },
+
+ trackScenery: function(){
+ var duration = this.sceneryTimer.currentTime()
+ if (this.scenery_id && duration > 1000) {
+ this.pushEvent([ "scenery", this.scenery_id, duration ])
+ }
+ this.scenery_id = null
+ this.sceneryTimer.reset()
+ },
+
+ //
+ // how long they spend in the room
+
+ changeRoom: function(opt){
+ var duration = this.roomTimer.currentTime()
+ if (this.room_id !== opt.room.id) {
+ if (this.room_id && duration > 5000) {
+ this.pushEvent([ "room", this.room_id, duration ])
+ }
+ this.roomTimer.start()
+ this.room_id = opt.room.id
+ }
+ },
+
+ //
+ // how many clicks per room
+
+ trackClick: function(opt){
+ this.clicks += 1
+ },
+
+ save: function () {
+ // possibly just push to google analytics
+ },
+
+ }
+
+ return exports
+})
+
+
+var Timer = Fiber.extend(function(base){
+ var exports = {
+
+ init: function(opt){
+ this.time = 0
+ },
+
+ reset: function(){
+ this.time = 0
+ },
+
+ start: function(){
+ this.time = Date.now()
+ },
+
+ currentTime: function(){
+ return this.time ? Date.now() - this.time : 0
+ },
+ }
+
+ return exports
+})
diff --git a/public/assets/javascripts/ui/reader/_router.js b/public/assets/javascripts/ui/reader/_router.js
new file mode 100644
index 0000000..f3b121b
--- /dev/null
+++ b/public/assets/javascripts/ui/reader/_router.js
@@ -0,0 +1,24 @@
+
+var SiteRouter = Router.extend({
+ el: "body",
+
+ initialize: function(){
+ app.launch()
+ if (app.unsupported) return
+
+ this.readerView = app.controller = new ReaderView()
+ this.readerView.load()
+
+ $("body").removeClass("loading")
+ }
+
+})
+
+var editor = {
+ permissions: new Permissions({
+ 'pick': true,
+ 'move': true,
+ 'resize': true,
+ 'destroy': false,
+ })
+}
diff --git a/public/assets/javascripts/ui/site/EditProfileModal.js b/public/assets/javascripts/ui/site/EditProfileModal.js
index b023923..d0e5d05 100644
--- a/public/assets/javascripts/ui/site/EditProfileModal.js
+++ b/public/assets/javascripts/ui/site/EditProfileModal.js
@@ -3,11 +3,14 @@ var EditProfileModal = ModalFormView.extend({
el: ".mediaDrawer.editProfile",
action: "/api/profile",
method: "put",
+
+ events: {
+ "click [data-role='changePasswordToggle']": 'togglePasswordFields'
+ },
load: function(){
this.reset()
$.get("/api/profile", function(data){
- console.log(data)
for (var i in data) {
this.$("[name='" + i + "']").val(data[i])
@@ -25,6 +28,10 @@ var EditProfileModal = ModalFormView.extend({
this.show()
}.bind(this))
},
+
+ togglePasswordFields: function(){
+ this.$("[data-role='changePasswordFields']").toggleClass("hidden")
+ },
validate: function(){
var errors = []
diff --git a/public/assets/javascripts/ui/site/EditSubscriptionModal.js b/public/assets/javascripts/ui/site/EditSubscriptionModal.js
new file mode 100644
index 0000000..c1dc9f8
--- /dev/null
+++ b/public/assets/javascripts/ui/site/EditSubscriptionModal.js
@@ -0,0 +1,290 @@
+
+var EditSubscriptionModal = ModalView.extend({
+ el: ".mediaDrawer.editSubscription",
+ action: "/api/subscription",
+ syncAction: "/api/subscription/sync",
+ updateAction: "/api/subscription",
+ destroyAction: "/api/subscription/destroy",
+
+ fixedClose: true,
+ editing: false,
+ subscriber: null,
+ tempSubscriber: null,
+
+ events: {
+ "click [data-role='addLayouts']": 'addLayouts',
+ "click [data-role='changePlan']": 'changePlan',
+ "click [data-role='cancelSubscription']": 'destroy',
+ "click .gear": 'sync',
+ "click .planList button": 'followLink',
+
+ "click [data-role=showEditMenu]": "editMode",
+
+ "click [data-role=closeMenu]": "resetMode",
+
+ "input [data-role=basicLayoutInput]": "updateQuantity",
+ "input [data-role=proLayoutInput]": "updateQuantity",
+ "click [data-role=saveChanges]": "saveChanges",
+
+ "change [name=planRadio]": "updatePlan",
+ "click [data-role=savePlan]": "savePlan",
+
+ "submit form": "preventDefault",
+ },
+
+ initialize: function(){
+ // this.parent = opt.parent
+ this.__super__.initialize.call(this)
+
+ // two sections
+ this.$freePlan = this.$(".freePlan")
+ this.$paidPlan = this.$(".paidPlan")
+
+ // subscription table
+ this.$planInfo = this.$(".planInfo")
+ this.$planRow = this.$(".planRow")
+ this.$basicLayoutRow = this.$(".basicLayoutRow")
+ this.$proLayoutRow = this.$(".proLayoutRow")
+ this.$totalRow = this.$(".totalRow")
+ this.$planList = this.$(".planList")
+
+ this.$billingInterval = this.$("[data-role=billingInterval]")
+
+ // plan stuff
+ this.$planName = this.$("[data-role=planName]")
+ this.$planCost = this.$("[data-role=planCost]")
+ this.$planTotal = this.$("[data-role=planTotal]")
+
+ this.$basicPlanName = this.$("[data-role=basicPlanName]")
+ this.$basicPlanCost = this.$("[data-role=basicPlanCost]")
+
+ this.$proPlanName = this.$("[data-role=proPlanName]")
+ this.$proPlanCost = this.$("[data-role=proPlanCost]")
+
+ // basic + pro layout stuff
+ this.$basicLayoutCost = this.$("[data-role=basicLayoutCost]")
+ this.$basicLayoutQuantity = this.$("[data-role=basicLayoutQuantity]")
+ this.$basicLayoutTotal = this.$("[data-role=basicLayoutTotal]")
+
+ this.$proLayoutCost = this.$("[data-role=proLayoutCost]")
+ this.$proLayoutQuantity = this.$("[data-role=proLayoutQuantity]")
+ this.$proLayoutTotal = this.$("[data-role=proLayoutTotal]")
+
+ // menus.. main menu
+ this.$showEditMenu = this.$("[data-role=showEditMenu]")
+ this.$cancelSubscription = this.$("[data-role=cancelSubscription]")
+
+ // three submenus
+ this.$editMenu = this.$("[data-role=editMenu]")
+ this.$planMenu = this.$("[data-role=planMenu]")
+
+ this.$buyLayouts = this.$("[data-role=buyLayouts]")
+ this.$closeMenu = this.$("[data-role=closeMenu]")
+ this.$changePlan = this.$("[data-role=changePlan]")
+
+ // input fields
+ this.$basicLayoutInput = this.$("[data-role=basicLayoutInput]")
+ this.$proLayoutInput = this.$("[data-role=proLayoutInput]")
+ this.$planRadio = this.$("[name=planRadio]")
+ this.$basicPlanInput = this.$("[data-role=basicPlanInput]")
+ this.$proPlanInput = this.$("[data-role=proPlanInput]")
+
+ this.$gear = this.$(".gear")
+ },
+
+ plan_levels: {
+ free: 0,
+ basic: 1,
+ pro: 2,
+ custom: 3,
+ artist: 4,
+ },
+
+ loaded: false,
+ load: function(){
+ if (this.loaded) { return this.show() }
+ $.get(this.action, this.didLoad.bind(this))
+ },
+ didLoad: function(data){
+ this.loaded = true
+ this.plans = data.plans
+ if (data.subscription) {
+ this.subscriber = data.subscription
+ }
+ else if (data.error) {
+ // ...no subscription found
+ this.subscriber = null
+ }
+ return this.show()
+ },
+ followLink: function(e){
+ e.preventDefault();
+ window.location.href = $(e.target).closest("a").attr("href")
+ },
+
+ show: function(){
+ this.$gear.removeClass("turning")
+ if (! this.subscriber) {
+ this.$freePlan.show()
+ this.$paidPlan.hide()
+ this.$planList.load("/partials/plans", function(){
+ this.$(".free_plan_info").remove()
+ this.__super__.show.call(this)
+ }.bind(this))
+ return
+ }
+
+ this.$freePlan.hide()
+ this.$paidPlan.show()
+
+ this.resetMode()
+
+ this.__super__.show.call(this)
+ },
+ reset: function(){
+ var subscriber = this.subscriber
+ var plan = this.getPlan(subscriber.plan_type)
+ this.displayTotals(subscriber, plan)
+ },
+ getPlan: function(plan_type){
+ return this.plans[ this.plan_levels[ plan_type ] ]
+ },
+ calculateTotals: function(subscriber, plan){
+ var t = {}
+ t.is_pro = subscriber.plan_type == "pro"
+ t.is_monthly = subscriber.plan_period == "monthly"
+ t.plan_price = t.is_monthly ? plan.monthly_price : plan.yearly_price
+ t.basic_layout_price = t.is_monthly ? plan.basic_layout_monthly_price : plan.basic_layout_yearly_price
+ t.basic_layout_total = subscriber.basic_layouts * t.basic_layout_price
+ t.pro_layout_price = t.is_monthly ? plan.pro_layout_monthly_price : plan.pro_layout_yearly_price
+ t.pro_layout_total = t.is_pro ? subscriber.pro_layouts * t.pro_layout_price : 0
+ t.plan_total = t.plan_price + t.basic_layout_total + t.pro_layout_total
+ return t
+ },
+ displayTotals: function(subscriber, plan){
+ var totals = this.calculateTotals(subscriber, plan)
+
+ this.$basicPlanName.html ( this.plans[1].name )
+ this.$proPlanName.html ( this.plans[2].name )
+ this.$basicPlanCost.toDollars ( totals.is_monthly ? this.plans[1].monthly_price : this.plans[2].yearly_price)
+ this.$proPlanCost.toDollars ( totals.is_monthly ? this.plans[2].monthly_price : this.plans[2].yearly_price)
+
+ this.$planName.html ( plan.name )
+ this.$planCost.toDollars ( totals.plan_price )
+
+ this.$billingInterval.html ( totals.is_monthly ? "mo." : "yr." )
+ this.$basicLayoutRow.toggle ( subscriber.basic_layouts > 0 )
+ this.$proLayoutRow.toggle ( totals.is_pro && subscriber.pro_layouts > 0)
+
+ this.$basicLayoutCost.toDollars ( totals.basic_layout_price )
+ this.$basicLayoutQuantity.html ( subscriber.basic_layouts )
+ this.$basicLayoutTotal.toDollars ( totals.basic_layout_total )
+
+ this.$proLayoutCost.toDollars ( totals.pro_layout_price )
+ this.$proLayoutQuantity.html ( subscriber.pro_layouts )
+ this.$proLayoutTotal.toDollars ( totals.pro_layout_total )
+
+ this.$planTotal.toDollars ( totals.plan_total )
+ },
+
+ editMode: function(e){
+ e && e.preventDefault()
+
+ this.editing = true
+ this.$el.addClass("editing")
+ this.tempSubscriber = defaults({}, this.subscriber)
+ this.$basicLayoutInput.val( this.subscriber.basic_layouts )
+ this.$proLayoutInput.val( this.subscriber.pro_layouts )
+ this.$basicLayoutRow.show()
+ this.$proLayoutRow.toggle(this.subscriber.plan_type == "pro")
+ switch (this.subscriber.plan_type) {
+ case 'basic': this.$basicPlanInput.prop('checked', true); break;
+ case 'pro': this.$proPlanInput.prop('checked', true); break;
+ }
+ },
+ resetMode: function(e){
+ e && e.preventDefault()
+ this.editing = false
+ this.$el.removeClass("editing")
+ this.reset()
+ },
+
+ updateQuantity: function(e){
+ e && e.preventDefault()
+ var plan = this.getPlan( this.tempSubscriber.plan_type )
+ this.tempSubscriber.basic_layouts = clamp( this.$basicLayoutInput.int() || 0, 0, 100)
+ this.tempSubscriber.pro_layouts = clamp( this.$proLayoutInput.int() || 0, 0, 100)
+
+ this.$basicLayoutInput.val(this.tempSubscriber.basic_layouts)
+ this.$proLayoutInput.val(this.tempSubscriber.pro_layouts)
+ this.displayTotals(this.tempSubscriber, plan)
+ this.$basicLayoutRow.show()
+ this.$proLayoutRow.toggle(this.tempSubscriber.plan_type == "pro")
+ },
+ saveChanges: function(e){
+ e && e.preventDefault()
+ var is_changed = false
+ var diff = {}
+ "plan_type basic_layouts pro_layouts".split(" ").forEach(function(field){
+ diff[field] = this.tempSubscriber[field]
+ if (this.tempSubscriber[field] != this.subscriber[field]) {
+ is_changed = true
+ }
+ }.bind(this))
+
+ if (is_changed) {
+ this.update(diff)
+ }
+ this.subscriber = this.tempSubscriber
+ this.resetMode()
+ },
+
+ updatePlan: function(e){
+ e && e.preventDefault()
+ this.tempSubscriber.plan_type = this.$("[name=planRadio]:checked").val()
+ this.updateQuantity()
+ },
+
+ sync: function(){
+ this.$gear.addClass("turning")
+ $.ajax({
+ url: this.syncAction,
+ type: "put",
+ data: { _csrf: $("[name=_csrf]").val() },
+ success: this.didLoad.bind(this)
+ })
+ },
+
+ update: function(data){
+ data['_csrf'] = $("[name=_csrf]").val()
+ this.$gear.addClass("turning")
+ $.ajax({
+ url: this.updateAction,
+ type: "put",
+ data: data,
+ success: function(data){
+ this.$gear.removeClass("turning")
+ }.bind(this)
+ })
+ },
+
+ destroy: function(e){
+ e.preventDefault()
+ var msg = "Are you sure you want to cancel your subscription?"
+ ConfirmModal.confirm(msg, function(){
+ $.ajax({
+ url: this.destroyAction,
+ type: "delete",
+ data: { _csrf: $("[name=_csrf]").val() },
+ success: function(data){
+ this.subscriber = null
+ this.didLoad(data)
+ }.bind(this)
+ })
+ }.bind(this),
+ function(){
+ this.show()
+ }.bind(this))
+ },
+
+})
diff --git a/public/assets/javascripts/ui/site/HomeView.js b/public/assets/javascripts/ui/site/HomeView.js
index 02f9ab9..20452bd 100644
--- a/public/assets/javascripts/ui/site/HomeView.js
+++ b/public/assets/javascripts/ui/site/HomeView.js
@@ -6,6 +6,32 @@ var HomeView = View.extend({
load: function() {
this.projectList = new ProjectList ()
- }
+
+ var iframe = $("#okplayer")[0]
+ var player = $f( iframe )
+ player.addEvent('ready', function(){
+ player.addEvent('play', function(){
+ player.api('setVolume', 1.0)
+ })
+ player.addEvent('finish', function(){
+ hide()
+ })
+ })
+ $('.hero .circle').click( function(){
+ $('.videoModal').css("display","table").addClass('active');
+ player.api('play')
+ })
+
+ $('.videoModal .ion-ios-close-empty').click( function(){
+ player.api('pause')
+ hide()
+ })
+
+ function hide() {
+ $('.videoModal').fadeOut(300, function(){
+ $('.videoModal').removeClass('active')
+ })
+ }
+ },
})
diff --git a/public/assets/javascripts/ui/site/LayoutsIndex.js b/public/assets/javascripts/ui/site/LayoutsIndex.js
new file mode 100644
index 0000000..f7272bb
--- /dev/null
+++ b/public/assets/javascripts/ui/site/LayoutsIndex.js
@@ -0,0 +1,85 @@
+
+var LayoutsIndex = View.extend({
+
+ initialize: function(){
+ this.$templates = this.$(".templates")
+ this.$templatesList = this.$(".templates-list")
+ this.$noTemplates = this.$(".no-templates")
+ this.$form = this.$("form")
+
+ this.$userTemplatesList = this.$(".userTemplatesList")
+ this.$blueprintsList = this.$(".blueprintsList")
+ this.$newBlueprintButton = this.$("[data-role='create-new-blueprint']")
+ },
+
+ load: function(type){
+ this.$templates.children("span").remove()
+
+ $.get(this.action, this.populate.bind(this))
+ },
+
+ populate: function(data){
+ if (! data.layouts.length) {
+ this.$templates.hide()
+ this.$form.hide()
+ this.$noTemplates.show()
+ }
+ this.$templatesList.empty()
+ data.layouts.forEach(function(room){
+ var $span = $("<span>")
+ $span.data("slug", room.slug)
+
+ var $label = $("<label>")
+ $label.html( room.name )
+
+ var $image = $("<span>")
+ $image.addClass("image").css("background-image", "url(" + room.photo + ")")
+
+ $span.append( $image )
+ $span.append( $label )
+
+ this.$templatesList.append($span)
+ }.bind(this))
+ this.show()
+ }
+
+})
+
+
+
+var ProjectsModal = ModalView.extend(LayoutsIndex.prototype).extend({
+ el: ".mediaDrawer.projects",
+
+ action: "/api/project",
+
+ events: {
+ "click .templates span": 'toggleActive',
+ "submit form": 'newProject',
+ },
+
+ populate: function(data){
+ if (! data.length) {
+ app.router.newProject()
+ }
+ else {
+ this.__super__.populate.call(this, data)
+ }
+ },
+
+ toggleActive: function(e){
+ e.preventDefault()
+ this.$(".templates .active").removeClass("active")
+ var $layout = $(e.currentTarget)
+ $layout.addClass("active")
+
+ // actually do
+ window.location.pathname = "/project/" + $layout.data("slug") + "/edit"
+ },
+
+ newProject: function(e){
+ e && e.preventDefault()
+ window.location.pathname = "/project/new"
+ }
+
+})
+
diff --git a/public/assets/javascripts/ui/site/LayoutsModal.js b/public/assets/javascripts/ui/site/LayoutsModal.js
index 2449465..0222077 100644
--- a/public/assets/javascripts/ui/site/LayoutsModal.js
+++ b/public/assets/javascripts/ui/site/LayoutsModal.js
@@ -1,127 +1,106 @@
+var LayoutsModal = ModalView.extend(LayoutsIndex.prototype).extend({
+ el: ".mediaDrawer.layouts",
-var LayoutsIndex = View.extend({
+ action: "/api/layout",
- initialize: function(){
- this.$templates = this.$(".templates")
- this.$noTemplates = this.$(".no-templates")
- this.$form = this.$("form")
+ events: {
+ "click [data-role='create-new-layout']": 'createNewLayout',
+ "click [data-role='create-new-blueprint']": 'createNewBlueprint',
+ "click .templates span": 'pick',
},
+
+ pick: function(e){
+ e.preventDefault()
+ var layout = $(e.currentTarget).data("slug")
+ var isBlueprint = $(e.currentTarget).data("blueprint")
- load: function(type){
- this.$templates.children("span").remove()
+ if (! layout || ! layout.length) return
- $.get(this.action, this.populate.bind(this))
+ if (isBlueprint) {
+ window.location.pathname = "/blueprint/" + layout
+ }
+ else {
+ window.location.pathname = "/layout/" + layout
+ }
+ },
+
+ createNewLayout: function(e){
+ e.preventDefault()
+ window.location.pathname = "/layout/new"
+ },
+
+ createNewBlueprint: function(e){
+ e.preventDefault()
+ window.location.pathname = "/blueprint/new"
},
populate: function(data){
- if (! data.length) {
+/*
+ if (data.user.plan_level < 1 && data.projectCount == 1) {
+ // show lockout message
+ }
+*/
+ if (! data.layouts.length) {
this.$templates.hide()
this.$form.hide()
this.$noTemplates.show()
}
- this.$templates.empty()
- data.forEach(function(room){
+ this.$templatesList.empty()
+ data.layouts.forEach(function(room){
var $span = $("<span>")
- // $span.html(JSON.stringify(room))
$span.data("slug", room.slug)
- $span.css("background-image", "url(" + room.photo + ")")
- $span.attr("data-name", room.name)
- this.$templates.append($span)
+ var $label = $("<label>")
+ $label.html( room.name )
+
+ var $image = $("<span>")
+ $image.addClass("image").css("background-image", "url(" + room.photo + ")")
+
+ $span.append( $image )
+ $span.append( $label )
+
+ this.$templatesList.append($span)
}.bind(this))
- console.log(this.$templates.html())
- this.show()
- }
-
-})
-
-var ProjectsModal = ModalView.extend(LayoutsIndex.prototype).extend({
- el: ".mediaDrawer.projects",
-
- action: "/api/project",
-
- events: {
- "click .templates span": 'toggleActive',
- "submit form": 'newProject',
- },
-
- populate: function(data){
- if (! data.length) {
- app.router.newProject()
- }
- else {
- this.__super__.populate.call(this, data)
- }
- },
-
- toggleActive: function(e){
- e.preventDefault()
- this.$(".templates .active").removeClass("active")
- var $layout = $(e.currentTarget)
- $layout.addClass("active")
-
- // actually do
- window.location.pathname = "/project/" + $layout.data("slug") + "/edit"
- },
-
- newProject: function(e){
- e && e.preventDefault()
- window.location.pathname = "/project/new"
- }
-
-})
-
-
-var LayoutsModal = ModalView.extend(LayoutsIndex.prototype).extend({
- el: ".mediaDrawer.layouts",
-
- action: "/api/layout",
- events: {
- "click .templates span": 'toggleActive',
- "submit form": 'newLayout',
- },
-
- toggleActive: function(e){
- e.preventDefault()
- this.$(".templates .active").removeClass("active")
- var $layout = $(e.currentTarget)
- $layout.addClass("active")
+ data.user_layouts.forEach(function(room){
+ var $span = $("<span>")
+ $span.data("slug", room.slug)
+
+ var $label = $("<label>")
+ $label.html( room.name )
+
+ var $image = $("<span>")
+ $image.addClass("image").css("background-image", "url(" + room.photo + ")")
+
+ $span.append( $image )
+ $span.append( $label )
+
+ this.$templatesList.append($span)
+ }.bind(this))
+
+ data.blueprints.forEach(function(blueprint){
+ var $span = $("<span>")
+ $span.data("slug", blueprint.slug)
+ $span.data("blueprint", true)
+
+ var $label = $("<label>")
+ $label.html( blueprint.name )
+
+ var $image = $("<span>")
+ $image.addClass("image").css("background-image", "url(" + blueprint.url + ")")
+
+ $span.append( $image )
+ $span.append( $label )
+
+ this.$templatesList.append($span)
+ }.bind(this))
- // actually do
- window.location.pathname = "/layout/" + $layout.data("slug")
+ this.show()
},
-
+
newLayout: function(e){
e && e.preventDefault()
window.location.pathname = "/layout/new"
}
})
-
-
-var NewProjectModal = ModalView.extend(LayoutsIndex.prototype).extend({
- el: ".mediaDrawer.newProject",
-
- action: "/api/layout",
-
- events: {
- "click .templates span": 'choose',
- "submit form": 'choose',
- },
-
- toggleActive: function(e){
- e.preventDefault()
- this.$(".templates .active").removeClass("active")
- $(e.currentTarget).addClass("active")
- },
-
- choose: function(e){
- e && e.preventDefault()
-// 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/ui/site/NewProjectModal.js b/public/assets/javascripts/ui/site/NewProjectModal.js
new file mode 100644
index 0000000..31675ba
--- /dev/null
+++ b/public/assets/javascripts/ui/site/NewProjectModal.js
@@ -0,0 +1,118 @@
+var NewProjectModal = ModalView.extend(LayoutsIndex.prototype).extend({
+ el: ".mediaDrawer.newProject",
+
+ action: "/api/layout",
+
+ events: {
+ "click [data-role='create-new-layout']": 'createNewLayout',
+ "click [data-role='create-new-blueprint']": 'createNewBlueprint',
+ "click .templates span": 'pick',
+ },
+
+ toggleActive: function(e){
+ e.preventDefault()
+ this.$(".templates .active").removeClass("active")
+ $(e.currentTarget).addClass("active")
+ },
+
+ pick: function(e){
+ e && e.preventDefault()
+// var layout = this.$(".templates .active").data("slug")
+ var layout = $(e.currentTarget).data("slug")
+ var isBlueprint = $(e.currentTarget).data("blueprint")
+ if (! layout || ! layout.length) return
+
+ if (isBlueprint) {
+ window.location.pathname = "/project/blueprint/" + layout
+ }
+ else {
+ window.location.pathname = "/project/new/" + layout
+ }
+ },
+
+ createNewLayout: function(e){
+ e.preventDefault()
+ window.location.pathname = "/project/new/empty"
+ },
+
+ createNewBlueprint: function(e){
+ e.preventDefault()
+ window.location.pathname = "/blueprint/new"
+ },
+
+ populate: function(data){
+/*
+ if (data.user.plan_level < 1 && data.projectCount == 1) {
+ // show lockout message
+ this.$newBlueprintButton.hide()
+ }
+*/
+ if (! data.layouts.length) {
+ this.$templates.hide()
+ this.$form.hide()
+ this.$noTemplates.show()
+ }
+ if (! data.blueprints.length) {
+ this.$blueprintsList.parent().hide()
+ }
+ if (! data.user_layouts.length) {
+ this.$userTemplatesList.parent().hide()
+ }
+
+ this.$templatesList.empty()
+ data.layouts.forEach(function(room){
+ var $span = $("<span>")
+ $span.data("slug", room.slug)
+
+ var $label = $("<label>")
+ $label.html( room.name )
+
+ var $image = $("<span>")
+ $image.addClass("image").css("background-image", "url(" + room.photo + ")")
+
+ $span.append( $image )
+ $span.append( $label )
+
+ this.$templatesList.append($span)
+ }.bind(this))
+
+ data.user_layouts.forEach(function(room){
+ var $span = $("<span>")
+ $span.data("slug", room.slug)
+
+ var $label = $("<label>")
+ $label.html( room.name )
+
+ var $image = $("<span>")
+ $image.addClass("image").css("background-image", "url(" + room.photo + ")")
+
+ $span.append( $image )
+ $span.append( $label )
+
+ this.$templatesList.append($span)
+ }.bind(this))
+
+ data.blueprints.forEach(function(blueprint){
+ if (! blueprint.slug) { return }
+
+ var $span = $("<span>")
+ $span.data("blueprint", true)
+ $span.data("slug", blueprint.slug)
+
+ var $label = $("<label>")
+ $label.html( blueprint.name )
+
+ var $image = $("<span>")
+ $image.addClass("image").css("background-image", "url(" + blueprint.url + ")")
+
+ $span.append( $image )
+ $span.append( $label )
+
+ this.$templatesList.append($span)
+ }.bind(this))
+
+ this.show()
+ },
+
+
+}) \ No newline at end of file
diff --git a/public/assets/javascripts/ui/site/ProfileView.js b/public/assets/javascripts/ui/site/ProfileView.js
index f3b35d9..8471abc 100644
--- a/public/assets/javascripts/ui/site/ProfileView.js
+++ b/public/assets/javascripts/ui/site/ProfileView.js
@@ -1,6 +1,12 @@
var ProfileView = View.extend({
+ el: ".profilepage",
+
+ events: {
+ "change #profile_avatar": "uploadAvatar",
+ },
+
initialize: function() {
},
@@ -12,6 +18,28 @@ var ProfileView = View.extend({
$(".bio").addClass(choice(classes));
this.projectList = new ProjectList ()
+ },
+
+ uploadAvatar: function(){
+ var fd = new FormData(), hasCSRF = false
+ var files = this.$("#profile_avatar")[0].files
+ if (! files.length) return
+
+ fd.append("avatar", files[0]);
+ fd.append("_csrf", $("[name=_csrf]").val())
+
+ var request = $.ajax({
+ url: "/api/profile",
+ type: "put",
+ data: fd,
+ dataType: "json",
+ processData: false,
+ contentType: false,
+ })
+
+ request.done($.proxy(function (response) {
+ window.location.href = "/profile"
+ }, this));
}
})
diff --git a/public/assets/javascripts/ui/site/ProjectList.js b/public/assets/javascripts/ui/site/ProjectList.js
index 993d805..076a674 100644
--- a/public/assets/javascripts/ui/site/ProjectList.js
+++ b/public/assets/javascripts/ui/site/ProjectList.js
@@ -1,16 +1,75 @@
+var projectListTimeout = null
var ProjectList = View.extend({
el: ".projectList",
events: {
- "mouseenter .room": 'spinOn',
- "mouseleave .room": 'spinOff',
+ "click .viewMore": 'viewMore',
+ "mouseenter .room": 'enter',
+ "mouseleave .room": 'leave',
},
initialize: function(){
+ this.$viewMore = this.$(".viewMore")
+ this.$(".images").each(function(){
+ $divs = $(this).children("div")
+ $divs.hide()
+ $divs.first().show()
+ $(this).data("index", 0)
+ })
},
+ timeout: null,
+ enter: function(e){
+ clearTimeout(projectListTimeout)
+ this.advance(e.currentTarget)
+ },
+
+ leave: function(e){
+ clearTimeout(projectListTimeout)
+ var $divs = $(e.currentTarget).find(".images div")
+ $divs.hide()
+ $divs.eq(0).show()
+ },
+
+ advance: function(el){
+ projectListTimeout = setTimeout(function(){
+ this.advance(el)
+ }.bind(this), 500)
+ var $images = $(el).find(".images")
+ var $divs = $images.children("div")
+ var index = $images.data("index")
+ var nextIndex = (index + 1) % $divs.length
+ $divs.eq(index).hide()
+ $divs.eq(nextIndex).show()
+ $images.data("index", nextIndex)
+ },
+
+ viewMore: function(e){
+ e.preventDefault()
+ var criteria = {}
+ criteria.offset = this.$(".projectItem").length
+ if (window.location.pathname == "/" || window.location.pathname.match("/home")) {
+ criteria.home = 1
+ }
+ else {
+ criteria.user_id = this.$(".projectItem").first().data("userid")
+ }
+
+ $.get("/api/project/paginate", criteria, function(data){
+ var offset = this.$viewMore.offset()
+ var $data = $(data)
+ var $els = $data.find(".projectItem")
+ $els.insertBefore( this.$viewMore )
+ if (! $data.find(".viewMore").length) {
+ this.$viewMore.hide()
+ }
+ $("body,html").animate({ scrollTop: offset.top - 80 }, 300)
+ }.bind(this))
+ },
+
+/*
spinOn: function(e){
var iframe = $(e.currentTarget).find("iframe").get('0')
if (! iframe) return
@@ -22,5 +81,6 @@ var ProjectList = View.extend({
if (! iframe) return
iframe.contentWindow.postMessage("spin-off", window.location.origin)
}
+*/
})
diff --git a/public/assets/javascripts/ui/site/StaffView.js b/public/assets/javascripts/ui/site/StaffView.js
index fdf39d2..97f86c2 100644
--- a/public/assets/javascripts/ui/site/StaffView.js
+++ b/public/assets/javascripts/ui/site/StaffView.js
@@ -3,14 +3,29 @@ var StaffView = View.extend({
events: {
"click #toggle-staff": "toggleStaff",
+ "click #toggle-featured": "toggleFeatured",
+ "click #toggle-stock": "toggleStock",
+ "click #toggle-artist": "toggleArtist",
},
initialize: function() {
this.$toggleStaff = $("#toggle-staff")
+ this.$toggleFeatured = $("#toggle-featured")
+ this.$toggleStock = $("#toggle-stock")
+ this.$toggleArtist = $("#toggle-artist")
this.$mediaEmbed = $("#media-embed")
if (this.$toggleStaff.length && this.$toggleStaff.data().isstaff) {
this.$toggleStaff.html("Is Staff")
}
+ if (this.$toggleFeatured.length && this.$toggleFeatured.data().featured) {
+ this.$toggleFeatured.html("Featured Project")
+ }
+ if (this.$toggleStock.length && this.$toggleStock.data().stock) {
+ this.$toggleStock.html("Layout is Stock")
+ }
+ if (this.$toggleArtist.length && this.$toggleArtist.data().isartist) {
+ this.$toggleArtist.html("Is Artist")
+ }
if (this.$mediaEmbed.length) {
var media = this.$mediaEmbed.data()
this.$mediaEmbed.html( Parser.tag( media ) )
@@ -44,6 +59,59 @@ var StaffView = View.extend({
}.bind(this)
})
}.bind(this))
- },
+ },
+ toggleFeatured: function(){
+ var state = ! this.$toggleFeatured.data().featured
+ $.ajax({
+ type: "put",
+ dataType: "json",
+ url: window.location.href + "/feature",
+ data: {
+ state: state,
+ _csrf: $("#_csrf").val(),
+ },
+ success: function(data){
+ this.$toggleFeatured.data("featured", data.state)
+ this.$toggleFeatured.html(data.state ? "Featured Project" : "Feature this project")
+ $("#isFeaturedProject").html(data.state ? "yes" : "no")
+ }.bind(this)
+ })
+ },
+
+ toggleStock: function(){
+ var state = ! this.$toggleStock.data().stock
+ $.ajax({
+ type: "put",
+ dataType: "json",
+ url: window.location.href + "/stock",
+ data: {
+ state: state,
+ _csrf: $("#_csrf").val(),
+ },
+ success: function(data){
+ this.$toggleStock.data("stock", data.state)
+ this.$toggleStock.html(data.state ? "Stock Layout" : "Make this layout Stock")
+ $("#isStockLayout").html(data.state ? "yes" : "no")
+ }.bind(this)
+ })
+ },
+
+ toggleArtist: function(){
+ var state = ! this.$toggleArtist.data().isartist
+ $.ajax({
+ type: "put",
+ dataType: "json",
+ url: window.location.href + "/artist",
+ data: {
+ state: state,
+ _csrf: $("#_csrf").val(),
+ },
+ success: function(data){
+ this.$toggleArtist.data("stock", data.state)
+ this.$toggleArtist.html(data.state ? "Is Artist" : "Make Artist")
+ $("#isArtist").html(data.state ? "yes" : "no")
+ }.bind(this)
+ })
+ },
})
diff --git a/public/assets/javascripts/ui/z_share.js b/public/assets/javascripts/ui/z_share.js
deleted file mode 100644
index d31aa89..0000000
--- a/public/assets/javascripts/ui/z_share.js
+++ /dev/null
@@ -1,25 +0,0 @@
-var share = {
- init: function(){
- share.bind()
- },
- bind: function(){
- $("#facebook").click(share.facebook)
- $("#twitter").click(share.twitter)
- },
- url: "http://vvalls.com/",
- facebook_msg: "",
- twitter_msg: "",
- openLink: function (url) {
- window.open(url, "_blank");
- },
- facebook: function () {
- var url = "https://www.facebook.com/share.php?u=" + encodeURIComponent(share.url) + "&t=" + encodeURIComponent(share.facebook_msg);
- share.openLink(url);
- return false;
- },
- twitter: function () {
- var url = "https://twitter.com/home?status=" + encodeURIComponent(share.url + " " + share.twitter_msg);
- share.openLink(url);
- return false;
- }
-}