664 lines
18 KiB
JavaScript
664 lines
18 KiB
JavaScript
/*
|
|
SpacedeckBoardArtifacts
|
|
This module contains functions dealing with absolute positioned Board Section Artifacts.
|
|
*/
|
|
|
|
var SpacedeckBoardArtifacts = {
|
|
update_board_artifact_viewmodel: function(a) {
|
|
var mt = this.artifact_major_type(a);
|
|
|
|
a.view = {
|
|
_id: a._id,
|
|
classes: this.artifact_classes(a),
|
|
style: this.artifact_style(a),
|
|
grid_style: this.artifact_style(a, true),
|
|
inner_style: this.artifact_inner_style(a),
|
|
text_cell_style: this.artifact_text_cell_style(a),
|
|
vector_svg: this.artifact_vector_svg(a),
|
|
payload_uri: a.payload_uri,
|
|
thumbnail_uri: this.artifact_thumbnail_uri(a),
|
|
major_type: mt,
|
|
text_blank: this.artifact_is_text_blank(a),
|
|
payload_alternatives: a.payload_alternatives,
|
|
filename: this.artifact_filename(a),
|
|
oembed_html: this.artifact_oembed_html(a),
|
|
link: this.artifact_link(a),
|
|
link_caption: this.artifact_link_caption(a),
|
|
interactive: 0
|
|
};
|
|
|
|
if ((mt == "audio" || mt == "video") && !a.player_view) {
|
|
a.player_view = {
|
|
state: "stop",
|
|
current_time_string: "",
|
|
total_time_string: "",
|
|
current_time_float: 0.0,
|
|
inpoint_float: 0.0,
|
|
outpoint_float: 0.0
|
|
};
|
|
}
|
|
|
|
if ("medium_for_object" in this) {
|
|
var medium = this.medium_for_object[a._id];
|
|
if (medium && a._id != this.editing_artifact_id) {
|
|
medium.value(a.description.toString());
|
|
}
|
|
}
|
|
},
|
|
|
|
is_artifact_audio: function(a) {
|
|
if (a) {
|
|
return a.mime.match("audio");
|
|
} else return false;
|
|
},
|
|
|
|
artifact_filename: function(a) {
|
|
if (a.payload_uri) {
|
|
return a.payload_uri.replace(/.*\//g,"");
|
|
} else {
|
|
return "";
|
|
}
|
|
},
|
|
|
|
artifact_link: function(a) {
|
|
if (a.link_uri) {
|
|
return a.link_uri;
|
|
} else {
|
|
return "";
|
|
}
|
|
},
|
|
|
|
artifact_link_caption: function(a) {
|
|
if (a.link_uri) {
|
|
var parts = a.link_uri.split("/");
|
|
// scheme://domain.foo/...
|
|
// 0 1 2
|
|
if (parts.length>2) {
|
|
return parts[2];
|
|
}
|
|
return "Link";
|
|
} else {
|
|
return "";
|
|
}
|
|
},
|
|
|
|
artifact_is_selected: function(a) {
|
|
if (!a) return false;
|
|
return !!this.selected_artifacts_dict[a._id];
|
|
},
|
|
|
|
artifact_is_text_blank: function(a) {
|
|
if (a.description) {
|
|
desc = a.description.toString();
|
|
var filtered = desc.replace(/<[^>]+>/g,"").replace(/\s/g,"");
|
|
return (filtered.length<1);
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
artifact_classes: function(a) {
|
|
clzs = ["artifact", "artifact-"+this.artifact_major_type(a), a.mime.replace("/","-")];
|
|
|
|
if (this.artifact_is_selected(a) && this.editing_artifact_id!=a._id) clzs.push("selected");
|
|
if (!a._id) clzs.push("creating");
|
|
|
|
if (a.align) clzs.push("align-"+a.align);
|
|
if (a.valign) clzs.push("align-"+a.valign);
|
|
|
|
clzs.push("state-"+a.state);
|
|
|
|
if (this.artifact_is_text_blank(a)) {
|
|
clzs.push("text-blank");
|
|
}
|
|
|
|
if (a.locked) {
|
|
clzs.push("locked");
|
|
}
|
|
|
|
return clzs.join(" ");
|
|
},
|
|
|
|
artifact_inner_style: function(a) {
|
|
var styles = [];
|
|
|
|
//if (a.style) {
|
|
|
|
var svg_style = ((a.mime.match("vector") || a.mime.match("shape")) && a.shape!="square");
|
|
|
|
if (!svg_style) {
|
|
if (a.stroke) {
|
|
styles.push("border-width:"+a.stroke+"px");
|
|
styles.push("border-style:"+(a.stroke_style||"solid"));
|
|
}
|
|
if (a.stroke_color) {
|
|
styles.push("border-color:"+a.stroke_color);
|
|
}
|
|
if (a.border_radius) {
|
|
styles.push("border-radius:"+a.border_radius+"px");
|
|
}
|
|
}
|
|
|
|
if (a.fill_color && !svg_style) {
|
|
styles.push("background-color:"+a.fill_color);
|
|
}
|
|
if (a.text_color) {
|
|
styles.push("color:"+a.text_color);
|
|
}
|
|
|
|
var filters = [];
|
|
|
|
if (!isNaN(a.brightness) && a.brightness != 100) {
|
|
filters.push("brightness("+a.brightness+"%)");
|
|
}
|
|
if (!isNaN(a.contrast) && a.contrast != 100) {
|
|
filters.push("contrast("+a.contrast+"%)");
|
|
}
|
|
if (!isNaN(a.opacity) && a.opacity != 100) {
|
|
filters.push("opacity("+a.opacity+"%)");
|
|
}
|
|
if (!isNaN(a.hue) && a.hue) {
|
|
filters.push("hue-rotate("+a.hue+"deg)");
|
|
}
|
|
if (!isNaN(a.saturation) && a.saturation != 100) {
|
|
filters.push("saturate("+a.saturation+"%)");
|
|
}
|
|
if (!isNaN(a.blur) && a.blur) {
|
|
filters.push("blur("+a.blur+"px)");
|
|
}
|
|
|
|
if (filters.length) {
|
|
styles.push("-webkit-filter:"+filters.join(" "));
|
|
styles.push("filter:"+filters.join(" "));
|
|
}
|
|
//}
|
|
|
|
return styles.join(";");
|
|
},
|
|
|
|
artifact_text_cell_style: function(a, for_text_editor) {
|
|
var styles = [];
|
|
|
|
if (a.padding_left) styles.push("padding-left:"+a.padding_left+"px");
|
|
if (a.padding_right) styles.push("padding-right:"+a.padding_right+"px");
|
|
if (a.padding_top) styles.push("padding-top:"+a.padding_top+"px");
|
|
if (a.padding_bottom) styles.push("padding-bottom:"+a.padding_bottom+"px");
|
|
|
|
return styles.join(";");
|
|
},
|
|
|
|
artifact_style: function(a, for_grid) {
|
|
var styles = [];
|
|
var z = 0;
|
|
|
|
z = a.z;
|
|
if (z<0) z=0; // fix negative z-index
|
|
|
|
styles = [
|
|
"left:" +a.x+"px",
|
|
"top:" +a.y+"px",
|
|
"width:" +a.w+"px",
|
|
"height:"+a.h+"px",
|
|
"z-index:"+z
|
|
];
|
|
|
|
if (a.margin_left) styles.push("margin-left:"+a.margin_left+"px");
|
|
if (a.margin_right) styles.push("margin-right:"+a.margin_right+"px");
|
|
if (a.margin_top) styles.push("margin-top:"+a.margin_top+"px");
|
|
if (a.margin_bottom) styles.push("margin-bottom:"+a.margin_bottom+"px");
|
|
|
|
// FIXME: via class logic?
|
|
if (a.mime.match("vector")) {
|
|
styles.push("overflow:visible");
|
|
}
|
|
|
|
return styles.join(";");
|
|
},
|
|
|
|
artifact_major_type: function(a) {
|
|
if (a.mime.match("oembed")) return "oembed";
|
|
if (a.mime.match("zone")) return "zone";
|
|
if (a.mime.match("svg")) return "svg";
|
|
if (a.mime.match("image")) return "image";
|
|
if (a.mime.match("pdf")) return "image";
|
|
if (a.mime.match("video")) return "video";
|
|
if (a.mime.match("audio")) return "audio";
|
|
if (a.mime.match("website")) return "website";
|
|
if (a.mime.match("vector")) return "vector";
|
|
if (a.mime.match("shape")) return "shape";
|
|
if (a.mime.match("placeholder")) return "placeholder";
|
|
if (a.mime.match("text") || a.mime.match("note")) return "text";
|
|
|
|
return "file";
|
|
},
|
|
|
|
artifact_thumbnail_uri: function(a) {
|
|
if (a.payload_thumbnail_big_uri && a.board) {
|
|
if (a.w>800) {
|
|
return a.payload_thumbnail_big_uri;
|
|
}
|
|
}
|
|
return a.payload_thumbnail_medium_uri || a.payload_thumbnail_big_uri || a.payload_thumbnail_web_uri || "";
|
|
},
|
|
|
|
artifact_oembed_html: function(a) {
|
|
if (this.artifact_major_type(a) != "oembed") return "";
|
|
|
|
var parts = a.mime.split("/")[1].split("-");
|
|
var type = parts[0];
|
|
var provider = parts[1];
|
|
|
|
if (!a.link_uri) {
|
|
console.log("missing meta / link_uri: ",a);
|
|
console.log("type/provider: ",type,provider);
|
|
return ("missing metadata: "+a._id);
|
|
}
|
|
|
|
if (provider=="youtube") {
|
|
var vid = a.link_uri.match(/(v=|\/)([a-zA-Z0-9\-_]{11})/);
|
|
if (vid && vid.length>2) {
|
|
var uri = "https://youtube.com/embed/"+vid[2];
|
|
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"?showinfo=0&rel=0&controls=0\"></iframe>";
|
|
} else return "Can't resolve: "+a.payload_uri;
|
|
|
|
} else if (provider=="dailymotion") {
|
|
var match = a.link_uri.match(/dailymotion.com\/video\/([^<]*)/);
|
|
if (match && match.length>1) {
|
|
var uri = "https://www.dailymotion.com/embed/video/"+match[1];
|
|
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"\"></iframe>";
|
|
} else return "Can't resolve: "+a.payload_uri;
|
|
|
|
} else if (provider=="vimeo") {
|
|
var match = a.link_uri.match(/https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/);
|
|
if (match) {
|
|
var uri = "https://player.vimeo.com/video/"+match[2];
|
|
return "<iframe frameborder=0 allowfullscreen src=\""+uri+"\"></iframe>";
|
|
} else return "Can't resolve: "+a.payload_uri;
|
|
|
|
} else if (provider=="soundcloud") {
|
|
return '<iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url='+a.link_uri.replace(":", "%3A")+'"></iframe>';
|
|
|
|
} else if (provider=="spacedeck") {
|
|
|
|
return ""; //<iframe frameborder=0 allowfullscreen src=\""+ a.meta.link_uri+"\"></iframe>
|
|
|
|
} else {
|
|
return "Don't know how to embed "+a.mime+".";
|
|
}
|
|
},
|
|
|
|
artifact_vector_svg: function(a) {
|
|
var mtype = this.artifact_major_type(a);
|
|
|
|
if (mtype != "vector" && mtype != "shape") return "";
|
|
|
|
var shape = a.shape || "";
|
|
var padding = 32 + a.stroke*2;
|
|
var path_svg;
|
|
var fill = "";
|
|
|
|
if (mtype == "vector") {
|
|
path_svg = render_vector_drawing(a, padding);
|
|
|
|
fill = "fill:none";
|
|
} else {
|
|
path_svg = render_vector_shape(a, padding);
|
|
fill = "fill:"+a.fill_color+";";
|
|
padding = 0;
|
|
}
|
|
var margin = padding;
|
|
|
|
var svg = "<svg xmlns='http://www.w3.org/2000/svg' width='"+(a.w+2*padding)+"' height='"+(a.h+2*padding)+"' ";
|
|
svg += "style='margin-left:"+(-margin)+"px;margin-top:"+(-margin)+"px;stroke-width:"+a.stroke+";stroke:"+a.stroke_color+";"+fill+"'>";
|
|
svg += path_svg;
|
|
svg += "</svg>";
|
|
|
|
return svg;
|
|
},
|
|
|
|
/* whiteboard layouting functions */
|
|
|
|
artifact_enclosing_rect: function(arts) {
|
|
if (arts.length==0) return null;
|
|
|
|
r = {
|
|
x1: parseInt(_.min(arts.map(function(a){return a.x}))),
|
|
y1: parseInt(_.min(arts.map(function(a){return a.y}))),
|
|
x2: parseInt(_.max(arts.map(function(a){return a.x+a.w}))),
|
|
y2: parseInt(_.max(arts.map(function(a){return a.y+a.h})))
|
|
};
|
|
r.x=r.x1;
|
|
r.y=r.y1;
|
|
r.w=r.x2-r.x1;
|
|
r.h=r.y2-r.y1;
|
|
|
|
return r;
|
|
},
|
|
|
|
artifact_selection_rect: function() {
|
|
return this.artifact_enclosing_rect(this.selected_artifacts());
|
|
},
|
|
|
|
rects_intersecting: function(r1,r2) {
|
|
if ( (r1.x+r1.w < r2.x)
|
|
|| (r1.x > r2.x+r2.w)
|
|
|| (r1.y+r1.h < r2.y)
|
|
|| (r1.y > r2.y+r2.h) ) return false;
|
|
return true;
|
|
},
|
|
|
|
artifacts_in_rect: function(rect) {
|
|
return _.filter(this.active_space_artifacts, function(a) {
|
|
return this.rects_intersecting(a, rect);
|
|
}.bind(this));
|
|
},
|
|
|
|
layout_stack_top: function() {
|
|
this.begin_transaction();
|
|
|
|
var rect = this.artifact_selection_rect();
|
|
var overlapping = _.filter(this.artifacts_in_rect(rect), function(a){return !this.is_selected(a)}.bind(this));
|
|
|
|
var max_z = _.max(overlapping,function(a){ return a.z; });
|
|
if (max_z.board) {
|
|
max_z = max_z.z + 1;
|
|
} else {
|
|
max_z = 1;
|
|
}
|
|
|
|
this.update_selected_artifacts(function(a) {
|
|
return { z: max_z };
|
|
});
|
|
},
|
|
|
|
layout_stack_bottom: function() {
|
|
this.begin_transaction();
|
|
|
|
var rect = this.artifact_selection_rect();
|
|
var overlapping = _.filter(this.artifacts_in_rect(rect), function(a){return !this.is_selected(a);}.bind(this));
|
|
|
|
var min_z = _.min(overlapping,function(a){ return a.z; });
|
|
if (min_z.board) {
|
|
min_z = min_z.z - 1;
|
|
} else {
|
|
min_z = 0;
|
|
}
|
|
var my_z = _.max(this.selected_artifacts(),function(a){ return a.z; });
|
|
if (my_z.board) {
|
|
my_z = my_z.z - 1;
|
|
} else {
|
|
my_z = 0;
|
|
}
|
|
|
|
// TODO: move all other items up in this case?
|
|
if (min_z < 0) {
|
|
this.update_artifacts(overlapping, function(a) {
|
|
return { z: (my_z + a.z + 1) };
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
this.update_selected_artifacts(function(a) {
|
|
return { z: min_z };
|
|
});
|
|
},
|
|
|
|
layout_align_left:function() {
|
|
this.begin_transaction();
|
|
|
|
var rect = this.artifact_selection_rect();
|
|
this.update_selected_artifacts(function(a) {
|
|
return { x: rect.x1 };
|
|
});
|
|
},
|
|
|
|
layout_align_top: function() {
|
|
this.begin_transaction();
|
|
|
|
var rect = this.artifact_selection_rect();
|
|
this.update_selected_artifacts(function(a) {
|
|
return { y: rect.y1 };
|
|
});
|
|
},
|
|
|
|
layout_align_right: function() {
|
|
this.begin_transaction();
|
|
|
|
var rect = this.artifact_selection_rect();
|
|
this.update_selected_artifacts(function(a) {
|
|
return { x: rect.x2 - a.w };
|
|
});
|
|
},
|
|
|
|
layout_align_bottom: function() {
|
|
this.begin_transaction();
|
|
|
|
var rect = this.artifact_selection_rect();
|
|
this.update_selected_artifacts(function(a) {
|
|
return { y: rect.y2 - a.h };
|
|
});
|
|
},
|
|
|
|
layout_align_center: function() {
|
|
this.begin_transaction();
|
|
|
|
var rect = this.artifact_selection_rect();
|
|
var cx = rect.x1 + (rect.x2-rect.x1)/2;
|
|
this.update_selected_artifacts(function(a) {
|
|
return { x: cx - a.w/2 };
|
|
});
|
|
},
|
|
|
|
layout_align_middle: function() {
|
|
this.begin_transaction();
|
|
|
|
var rect = this.artifact_selection_rect();
|
|
var cy = rect.y1 + (rect.y2-rect.y1)/2;
|
|
this.update_selected_artifacts(function(a) {
|
|
return { y: cy - a.h/2 };
|
|
});
|
|
},
|
|
|
|
layout_match_size_horiz:function() {
|
|
this.begin_transaction();
|
|
|
|
var arts = this.selected_artifacts();
|
|
if (arts.length<2) return;
|
|
|
|
var totalw = _.reduce(arts, function(sum, a) { return sum + a.w }, 0);
|
|
var avgw = totalw / arts.length;
|
|
|
|
this.update_selected_artifacts(function(a) {
|
|
return { w: avgw };
|
|
});
|
|
},
|
|
|
|
layout_match_size_vert:function() {
|
|
this.begin_transaction();
|
|
|
|
var arts = this.selected_artifacts();
|
|
if (arts.length<2) return;
|
|
|
|
var totalh = _.reduce(arts, function(sum, a) { return sum + a.h }, 0);
|
|
var avgh = totalh / arts.length;
|
|
|
|
this.update_selected_artifacts(function(a) {
|
|
return { h: avgh };
|
|
});
|
|
},
|
|
|
|
layout_match_size_both:function() {
|
|
this.layout_match_size_horiz();
|
|
this.layout_match_size_vert();
|
|
},
|
|
|
|
layout_distribute_horizontal: function() {
|
|
this.begin_transaction();
|
|
|
|
var selected = this.selected_artifacts();
|
|
if (selected.length<3) return;
|
|
|
|
var sorted = _.sortBy(selected, function(a) { return a.x });
|
|
var startx = sorted[0].x + sorted[0].w/2;
|
|
var stopx = _.last(sorted).x + _.last(sorted).w/2;
|
|
var step = (stopx-startx)/(sorted.length-1);
|
|
|
|
for (var i=1; i<sorted.length-1; i++) {
|
|
var a = sorted[i];
|
|
var x = startx + step*i - a.w/2;
|
|
this.update_artifacts([a],function(a) {
|
|
return { x: x }
|
|
});
|
|
}
|
|
},
|
|
|
|
layout_distribute_vertical: function() {
|
|
this.begin_transaction();
|
|
|
|
var selected = this.selected_artifacts();
|
|
if (selected.length<3) return;
|
|
|
|
var sorted = _.sortBy(selected, function(a) { return a.y });
|
|
var starty = sorted[0].y + sorted[0].h/2;
|
|
var stopy = _.last(sorted).y + _.last(sorted).h/2;
|
|
var step = (stopy-starty)/(sorted.length-1);
|
|
|
|
for (var i=1; i<sorted.length-1; i++) {
|
|
var a = sorted[i];
|
|
var y = starty + step*i - a.h/2;
|
|
this.update_artifacts([a],function(a) {
|
|
return { y: y }
|
|
});
|
|
}
|
|
},
|
|
|
|
layout_distribute_horizontal_spacing: function() {
|
|
this.begin_transaction();
|
|
|
|
var selected = this.selected_artifacts();
|
|
if (selected.length<3) return;
|
|
|
|
var sorted = _.sortBy(selected, function(a) { return a.x });
|
|
var startx = sorted[0].x;
|
|
var stopx = _.last(sorted).x + _.last(sorted).w;
|
|
var range = stopx - startx;
|
|
var totalw = _.reduce(sorted, function(sum, a) { return sum + a.w }, 0);
|
|
var avgs = (range - totalw) / (sorted.length-1);
|
|
var prevend = startx + sorted[0].w;
|
|
|
|
for (var i=1; i<sorted.length-1; i++) {
|
|
var a = sorted[i];
|
|
var x = prevend + avgs;
|
|
this.update_artifacts([a],function(a) {
|
|
return { x: x }
|
|
});
|
|
prevend = x+a.w;
|
|
}
|
|
},
|
|
|
|
layout_distribute_vertical_spacing: function() {
|
|
this.begin_transaction();
|
|
|
|
var selected = this.selected_artifacts();
|
|
if (selected.length<3) return;
|
|
|
|
var sorted = _.sortBy(selected, function(a) { return a.y });
|
|
var starty = sorted[0].y;
|
|
var stopy = _.last(sorted).y + _.last(sorted).h;
|
|
var range = stopy - starty;
|
|
var totalh = _.reduce(sorted, function(sum, a) { return sum + a.h }, 0);
|
|
var avgs = (range - totalh) / (sorted.length-1);
|
|
var prevend = starty + sorted[0].h;
|
|
|
|
for (var i=1; i<sorted.length-1; i++) {
|
|
var a = sorted[i];
|
|
var y = prevend + avgs;
|
|
this.update_artifacts([a],function(a) {
|
|
return { y: y }
|
|
});
|
|
prevend = y+a.h;
|
|
}
|
|
},
|
|
|
|
layout_auto: function() {
|
|
this.begin_transaction();
|
|
|
|
var selected = this.selected_artifacts();
|
|
if (selected.length<2) return;
|
|
|
|
var sorted = _.sortBy(selected, function(a) { return a.x+a.y*this.active_space.advanced.width }.bind(this));
|
|
|
|
var minx = sorted[0].x;
|
|
var miny = sorted[0].y;
|
|
|
|
var sorted = _.sortBy(selected, function(a) { return -Math.max(a.w,a.h) }.bind(this));
|
|
|
|
var blocks = [];
|
|
|
|
for (var i=0; i<sorted.length; i++) {
|
|
var a = sorted[i];
|
|
blocks.push({
|
|
w: a.w,
|
|
h: a.h,
|
|
a: a
|
|
});
|
|
}
|
|
|
|
var packer = new GrowingPacker();
|
|
packer.fit(blocks);
|
|
|
|
for (var i=0; i<blocks.length; i++) {
|
|
var block = blocks[i];
|
|
if (block.fit) {
|
|
var a = block.a;
|
|
this.update_artifacts([a],function(a) {
|
|
return {
|
|
x: minx+block.fit.x,
|
|
y: miny+block.fit.y
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
show_artifact_comments: function(evt) {
|
|
evt.preventDefault();
|
|
evt.stopPropagation();
|
|
|
|
var artifact = this.selected_artifacts()[0];
|
|
|
|
this.selected_artifact = artifact;
|
|
this.activate_modal('artifact');
|
|
},
|
|
|
|
create_artifact_comment: function(artifact, comment) {
|
|
var data = {
|
|
artifact_id: artifact._id,
|
|
space_id: this.active_space._id,
|
|
message: comment,
|
|
user: this.user
|
|
};
|
|
|
|
save_comment(this.active_space._id, data, function(comment) {
|
|
this.active_space_messages.push(comment);
|
|
this.artifact_comment = "";
|
|
}.bind(this), function(xhr){
|
|
console.error(xhr);
|
|
}.bind(this));
|
|
},
|
|
|
|
remove_artifact_comment: function(comment) {
|
|
delete_comment(this.active_space._id, comment._id, function(comment) {
|
|
this.active_space_messages.pop(comment);
|
|
}.bind(this), function(xhr){
|
|
console.error(xhr);
|
|
}.bind(this));
|
|
}
|
|
|
|
}
|
|
|
|
if (typeof(window) == 'undefined') {
|
|
exports.SpacedeckBoardArtifacts = SpacedeckBoardArtifacts;
|
|
}
|