initial commit.
This commit is contained in:
568
public/javascripts/spacedeck_directives.js
Normal file
568
public/javascripts/spacedeck_directives.js
Normal file
@@ -0,0 +1,568 @@
|
||||
|
||||
/*
|
||||
Spacedeck Directives
|
||||
This module registers custom Vue directives for Spacedeck.
|
||||
*/
|
||||
|
||||
function setup_directives() {
|
||||
Vue.directive('clipboard', {
|
||||
bind: function () {
|
||||
this.clipboard = new Clipboard(".clipboard-btn");
|
||||
},
|
||||
update: function (value) {
|
||||
},
|
||||
unbind: function () {
|
||||
this.clipboard.destroy()
|
||||
}
|
||||
});
|
||||
|
||||
Vue.directive('t', {
|
||||
update: function (value, key) {
|
||||
this.el.innerHTML = key;
|
||||
}
|
||||
});
|
||||
|
||||
if ('ontouchstart' in window) {
|
||||
var edown = "touchstart";
|
||||
var emove = "touchmove";
|
||||
var eup = "touchend";
|
||||
} else {
|
||||
var edown = "mousedown";
|
||||
var emove = "mousemove";
|
||||
var eup = "mouseup";
|
||||
}
|
||||
|
||||
Vue.directive('videoplayer', {
|
||||
update: function (a) {
|
||||
var el = this.el;
|
||||
var scope = this.vm.$root;
|
||||
var video = el.querySelectorAll("video")[0];
|
||||
var play_button = el.querySelectorAll(".play")[0];
|
||||
var pause_button = el.querySelectorAll(".pause")[0];
|
||||
var stop_button = el.querySelectorAll(".stop")[0];
|
||||
var player_state = "stop";
|
||||
|
||||
var update_view = function() {
|
||||
try {
|
||||
if (!a.player_view) { a.player_view = {} };
|
||||
a.player_view.state = player_state;
|
||||
} catch (e) {
|
||||
// catch InvalidStateError
|
||||
}
|
||||
}
|
||||
|
||||
var play_func = function() {
|
||||
video.play();
|
||||
player_state = "playing";
|
||||
update_view();
|
||||
}
|
||||
|
||||
var pause_func = function() {
|
||||
try {
|
||||
video.pause();
|
||||
player_state = "paused";
|
||||
update_view();
|
||||
} catch (e) {
|
||||
// catch InvalidStateError
|
||||
}
|
||||
}
|
||||
|
||||
var stop_func = function() {
|
||||
try {
|
||||
player_state = "stop";
|
||||
video.pause();
|
||||
video.currentTime = 0;
|
||||
update_view();
|
||||
|
||||
} catch (e) {
|
||||
// catch InvalidStateError
|
||||
}
|
||||
}
|
||||
|
||||
el.addEventListener("remote_play",play_func);
|
||||
el.addEventListener("remote_pause",pause_func);
|
||||
el.addEventListener("remote_stop",stop_func);
|
||||
|
||||
play_button.addEventListener(edown, function(evt) {
|
||||
try {
|
||||
play_func();
|
||||
spacedeck.presenter_send_media_action(a._id,"video","play",video.currentTime);
|
||||
} catch (e) {
|
||||
// catch InvalidStateError
|
||||
}
|
||||
}, false);
|
||||
|
||||
pause_button.addEventListener(edown, function(evt) {
|
||||
pause_func();
|
||||
spacedeck.presenter_send_media_action(a._id,"video","pause",video.currentTime);
|
||||
}, false);
|
||||
|
||||
stop_button.addEventListener(edown, function(evt) {
|
||||
stop_func();
|
||||
spacedeck.presenter_send_media_action(a._id,"video","stop",0);
|
||||
}, false);
|
||||
}
|
||||
});
|
||||
|
||||
Vue.directive('audioplayer', {
|
||||
update: function (a) {
|
||||
var el = this.el;
|
||||
var scope = this.vm.$root;
|
||||
var play_button = el.querySelectorAll(".play")[0];
|
||||
var pause_button = el.querySelectorAll(".pause")[0];
|
||||
var stop_button = el.querySelectorAll(".stop")[0];
|
||||
var timeline = el.querySelectorAll(".timeline")[0];
|
||||
var set_inpoint = el.querySelectorAll(".set-inpoint")[0];
|
||||
var set_outpoint = el.querySelectorAll(".set-outpoint")[0];
|
||||
var reset_points = el.querySelectorAll(".reset-points")[0];
|
||||
|
||||
var player_state = "stop";
|
||||
|
||||
var play_from = 0.0;
|
||||
var play_to = 0.0;
|
||||
|
||||
var audio = el.querySelectorAll("audio")[0];
|
||||
|
||||
var update_markers = function() {
|
||||
try {
|
||||
if (a.meta) {
|
||||
if (!a.meta.play_to) a.meta.play_to = audio.duration;
|
||||
play_from = parseFloat(a.meta.play_from) || 0.0;
|
||||
play_to = parseFloat(a.meta.play_to) || 0.0;
|
||||
} else {
|
||||
play_from = 0.0;
|
||||
play_to = parseFloat(audio.duration) || 0.0;
|
||||
a.meta = {};
|
||||
}
|
||||
} catch (e) {
|
||||
// catch InvalidStateError
|
||||
}
|
||||
}
|
||||
|
||||
var update_view = function() {
|
||||
try {
|
||||
if (!a.player_view) { a.player_view = {} };
|
||||
a.player_view.state = player_state;
|
||||
a.player_view.total_time_string = format_time(audio.duration);
|
||||
a.player_view.current_time_string = format_time(audio.currentTime);
|
||||
a.player_view.current_time_float = audio.currentTime/audio.duration;
|
||||
a.player_view.inpoint_float = play_from/audio.duration;
|
||||
a.player_view.outpoint_float = play_to/audio.duration;
|
||||
a.player_view.duration = audio.duration;
|
||||
} catch (e) {
|
||||
// catch InvalidStateError
|
||||
}
|
||||
}
|
||||
|
||||
var pause_audio = function() {
|
||||
try {
|
||||
audio.pause();
|
||||
player_state = "paused";
|
||||
} catch (e) {
|
||||
// catch InvalidStateError
|
||||
}
|
||||
update_view();
|
||||
}
|
||||
|
||||
var stop_audio = function() {
|
||||
try {
|
||||
audio.currentTime = play_from;
|
||||
audio.pause();
|
||||
player_state = "stop";
|
||||
} catch (e) {
|
||||
// catch InvalidStateError
|
||||
}
|
||||
update_view();
|
||||
}
|
||||
|
||||
update_view();
|
||||
|
||||
audio.addEventListener("loadedmetadata", function(evt) {
|
||||
update_markers();
|
||||
update_view();
|
||||
}, false);
|
||||
|
||||
audio.addEventListener("timeupdate", function(evt) {
|
||||
try {
|
||||
update_markers();
|
||||
if (audio.currentTime >= play_to && player_state == "playing") stop_audio();
|
||||
update_view();
|
||||
} catch (e) {
|
||||
// catch InvalidStateError
|
||||
}
|
||||
}, false);
|
||||
|
||||
var play_func = function() {
|
||||
if (player_state == "stop") {
|
||||
audio.currentTime = play_from;
|
||||
}
|
||||
|
||||
player_state = "playing";
|
||||
update_markers();
|
||||
audio.play();
|
||||
update_view();
|
||||
}
|
||||
|
||||
var pause_func = function() {
|
||||
pause_audio();
|
||||
update_view();
|
||||
}
|
||||
|
||||
var stop_func = function() {
|
||||
stop_audio();
|
||||
update_view();
|
||||
}
|
||||
|
||||
el.addEventListener("remote_play",play_func);
|
||||
el.addEventListener("remote_pause",pause_func);
|
||||
el.addEventListener("remote_stop",stop_func);
|
||||
|
||||
play_button.addEventListener(edown, function(evt) {
|
||||
try {
|
||||
play_func();
|
||||
spacedeck.presenter_send_media_action(a._id,"audio","play",audio.currentTime);
|
||||
} catch (e) {
|
||||
// catch InvalidStateError
|
||||
}
|
||||
}, false);
|
||||
|
||||
pause_button.addEventListener(edown, function(evt) {
|
||||
pause_func();
|
||||
spacedeck.presenter_send_media_action(a._id,"audio","pause",audio.currentTime);
|
||||
}, false);
|
||||
|
||||
stop_button.addEventListener(edown, function(evt) {
|
||||
stop_func();
|
||||
spacedeck.presenter_send_media_action(a._id,"audio","stop",0);
|
||||
}, false);
|
||||
|
||||
timeline.addEventListener(edown, function(evt) {
|
||||
var clicked_time = (parseFloat(evt.offsetX)/evt.currentTarget.offsetWidth)*audio.duration;
|
||||
if (isNaN(clicked_time)) {
|
||||
clicked_time = 0.0;
|
||||
}
|
||||
try {
|
||||
audio.currentTime = clicked_time;
|
||||
} catch (e) {
|
||||
// catch InvalidStateErrors
|
||||
}
|
||||
}, false);
|
||||
|
||||
set_inpoint.addEventListener(edown, function(evt) {
|
||||
if (!a.meta) a.meta = {};
|
||||
|
||||
a.meta.play_from = audio.currentTime;
|
||||
if (a.meta.play_to<a.meta.play_from) a.meta.play_to = audio.duration;
|
||||
update_markers();
|
||||
stop_audio();
|
||||
update_view();
|
||||
scope.save_artifact(a);
|
||||
}, false);
|
||||
|
||||
set_outpoint.addEventListener(edown, function(evt) {
|
||||
if (!a.meta) a.meta = {};
|
||||
|
||||
a.meta.play_to = audio.currentTime;
|
||||
if (a.meta.play_to<a.meta.play_from) a.meta.play_from = 0.0;
|
||||
update_markers();
|
||||
stop_audio();
|
||||
update_view();
|
||||
scope.save_artifact(a);
|
||||
}, false);
|
||||
|
||||
reset_points.addEventListener(edown, function(evt) {
|
||||
if (!a.meta) a.meta = {};
|
||||
|
||||
a.meta.play_from = 0.0;
|
||||
a.meta.play_to = audio.duration;
|
||||
update_markers();
|
||||
stop_audio();
|
||||
update_view();
|
||||
scope.save_artifact(a);
|
||||
}, false);
|
||||
}
|
||||
});
|
||||
|
||||
Vue.directive('sd-richtext', {
|
||||
twoWay: true,
|
||||
update: function(obj) {
|
||||
this.mode = 'rich';
|
||||
|
||||
$(this.el).addClass("text-editing");
|
||||
|
||||
this.medium = new Medium({
|
||||
element: this.el,
|
||||
mode: Medium.richMode,
|
||||
attributes: {
|
||||
remove: ['class','href','onclick','onmousedown','onmouseup']
|
||||
},
|
||||
});
|
||||
this.medium.value(obj.description);
|
||||
this.medium.element.addEventListener('keyup', function() {
|
||||
obj.description = this.medium.value();
|
||||
spacedeck.queue_artifact_for_save(obj);
|
||||
}.bind(this));
|
||||
|
||||
spacedeck.medium_for_object[obj._id] = this.medium;
|
||||
}
|
||||
});
|
||||
|
||||
Vue.directive('focus', {
|
||||
bind: function () {
|
||||
var el = this.el;
|
||||
window.setTimeout(function() {
|
||||
if (el.contentEditable && el.contentEditable!="inherit") {
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(el);
|
||||
} else {
|
||||
el.focus();
|
||||
el.select();
|
||||
}
|
||||
}, 500);
|
||||
},
|
||||
});
|
||||
|
||||
Vue.directive('sd-draggable', {
|
||||
update: function(data) {
|
||||
var el = this.el;
|
||||
|
||||
el.addEventListener(
|
||||
'dragstart',
|
||||
function(evt) {
|
||||
if ($(el).find(".text-editing").length) {
|
||||
// FIXME: technical debt
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
evt.dataTransfer.setData('application/json', JSON.stringify(data));
|
||||
$(el).addClass("dragging");
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Vue.directive('sd-droppable', {
|
||||
isFn: true,
|
||||
bind: function() {
|
||||
var el = this.el;
|
||||
var expression = this.expression;
|
||||
var parts = expression.split(";");
|
||||
var func_key = parts[0];
|
||||
var data_key = parts[1];
|
||||
|
||||
el.addEventListener(
|
||||
'dragover',
|
||||
function(e) {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
// allows us to drop
|
||||
if (e.preventDefault) e.preventDefault();
|
||||
el.classList.add('over');
|
||||
|
||||
return false;
|
||||
}.bind(this),
|
||||
false
|
||||
);
|
||||
|
||||
el.addEventListener(
|
||||
'dragenter',
|
||||
function(e) {
|
||||
el.classList.add('over');
|
||||
|
||||
return false;
|
||||
}.bind(this),
|
||||
false
|
||||
);
|
||||
|
||||
el.addEventListener(
|
||||
'dragleave',
|
||||
function(e) {
|
||||
el.classList.remove('over');
|
||||
return false;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
el.addEventListener(
|
||||
'drop',
|
||||
function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
$(e.currentTarget).find(".over").removeClass('over');
|
||||
$(e.currentTarget).find(".dragging").removeClass('dragging');
|
||||
|
||||
var func = this.vm.$root[func_key].bind(this.vm.$root);
|
||||
if (this._scope) {
|
||||
var obj = this._scope[data_key];
|
||||
} else {
|
||||
var obj = this.vm[data_key];
|
||||
}
|
||||
func(e, obj);
|
||||
|
||||
return false;
|
||||
}.bind(this),
|
||||
false
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Vue.directive('sd-fader', {
|
||||
bind: function (section) {
|
||||
|
||||
function clamp(v, mn, mx) {
|
||||
return Math.max(mn,Math.min(mx,v));
|
||||
}
|
||||
|
||||
var scope = this.vm.$root;
|
||||
|
||||
this.fader_state = "idle";
|
||||
this.fader_mx = 0;
|
||||
this.fader_my = 0;
|
||||
|
||||
var $el = $(this.el);
|
||||
|
||||
var handle = $el.find(".fader-selector");
|
||||
var indicator = $el.find(".fader-indicator");
|
||||
var constraint = $el.find(".fader-constraint");
|
||||
if (!constraint.length) constraint = $el;
|
||||
|
||||
var fader_var_x = $el.attr("sd-fader-var-x");
|
||||
var fader_var_y = $el.attr("sd-fader-var-y");
|
||||
|
||||
var knob_size = 0;
|
||||
var minx = 0;
|
||||
var miny = 0;
|
||||
var maxx = 0;
|
||||
var maxy = 0;
|
||||
|
||||
var nx = 0;
|
||||
if (xfader) nx = scope.$get(fader_var_x);
|
||||
var ny = 0;
|
||||
if (yfader) ny = scope.$get(fader_var_y);
|
||||
|
||||
var xfader = !!fader_var_x;
|
||||
var yfader = !!fader_var_y;
|
||||
var encoder = !handle[0];
|
||||
|
||||
var step = parseFloat($el.attr("sd-fader-step"))||1;
|
||||
var sensitivity = parseFloat($el.attr("sd-fader-sens"))||1;
|
||||
|
||||
var discover_minmax = function() {
|
||||
minx = (parseInt($el.attr("sd-fader-min-x"))||0);
|
||||
miny = (parseInt($el.attr("sd-fader-min-y"))||0);
|
||||
maxx = parseInt($el.attr("sd-fader-max-x"))||(constraint.width() - 1);
|
||||
maxy = parseInt($el.attr("sd-fader-max-y"))||(constraint.height() - 1);
|
||||
}
|
||||
|
||||
var position_handle = function() {
|
||||
discover_minmax();
|
||||
if (!nx || isNaN(nx)) nx = 0;
|
||||
if (!ny || isNaN(ny)) ny = 0;
|
||||
|
||||
if (handle[0]) {
|
||||
if (xfader) handle[0].style.left = nx+"px";
|
||||
if (yfader) handle[0].style.top = (maxy-ny)+"px";
|
||||
}
|
||||
|
||||
if (indicator[0]) {
|
||||
indicator[0].style.height = ny+"px";
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
var move_handle = function(dx,dy) {
|
||||
discover_minmax();
|
||||
if (xfader) {
|
||||
nx = clamp(dx, minx, maxx);
|
||||
scope.$set(fader_var_x, nx);
|
||||
}
|
||||
|
||||
if (yfader) {
|
||||
ny = clamp(dy, miny, maxy);
|
||||
if (step<1) ny = ny.toFixed(1); // float precision hack
|
||||
|
||||
scope.$set(fader_var_y, ny);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
var handle_move = function(evt) {
|
||||
evt = fixup_touches(evt);
|
||||
|
||||
var dx = parseInt((evt.pageX - this.fader_mx) * sensitivity);
|
||||
var dy = parseInt((evt.pageY - this.fader_my) * sensitivity);
|
||||
|
||||
dx *= step;
|
||||
dy *= step;
|
||||
|
||||
move_handle(this.fader_oldx+dx,this.fader_oldy-dy);
|
||||
}.bind(this);
|
||||
|
||||
var handle_up = function(evt) {
|
||||
this.fader_state = "idle";
|
||||
|
||||
$("body").off(emove, handle_move);
|
||||
$("body").off("mouseleave "+eup+" blur", handle_up);
|
||||
|
||||
window._sd_fader_moving = false; // signal for other event systems
|
||||
}.bind(this);
|
||||
|
||||
function prevent_event(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
};
|
||||
|
||||
$el.on(edown,function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
|
||||
evt = fixup_touches(evt);
|
||||
var offset = $(evt.target).offset();
|
||||
|
||||
this.fader_state = "drag";
|
||||
if (!encoder) {
|
||||
move_handle(evt.pageX-offset.left, maxy - (evt.pageY - offset.top) + knob_size/2);
|
||||
}
|
||||
|
||||
if (yfader) {
|
||||
ny = scope.$get(fader_var_y);
|
||||
}
|
||||
|
||||
$("body").on(emove, handle_move);
|
||||
$("body").on("mouseleave "+eup+" blur", handle_up);
|
||||
|
||||
this.fader_mx = evt.pageX;
|
||||
this.fader_my = evt.pageY;
|
||||
this.fader_oldx = nx||0;
|
||||
this.fader_oldy = ny||0;
|
||||
|
||||
window._sd_fader_moving = true; // signal for other event systems
|
||||
}.bind(this));
|
||||
|
||||
// initial state
|
||||
position_handle();
|
||||
|
||||
if (xfader) {
|
||||
scope.$watch(fader_var_x, function(a) {
|
||||
nx = parseInt(scope.$get(fader_var_x));
|
||||
position_handle();
|
||||
});
|
||||
}
|
||||
|
||||
if (yfader) {
|
||||
scope.$watch(fader_var_y, function(a) {
|
||||
ny = parseInt(scope.$get(fader_var_y));
|
||||
position_handle();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
var scope = this.vm.$root;
|
||||
var $el = $(this.el);
|
||||
var fader_var_x = $el.attr("sd-fader-var-x");
|
||||
var fader_var_y = $el.attr("sd-fader-var-y");
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user