App-SlideServer
view release on metacpan or search on metacpan
share/public/slides.js view on Meta::CPAN
this.cur_step= 0;
}
Slide.prototype.parseSteps= function(spec) {
var show_list= (""+spec).split(',');
for (var i= 0; i < show_list.length; i++) {
show_list[i]= show_list[i].split(/-/);
show_list[i][0]= parseInt(show_list[i][0]);
if (show_list[i].length > 1)
show_list[i][1]= parseInt(show_list[i][1]);
}
return show_list;
}
Slide.prototype.scaleTo= function(viewport_w, viewport_h) {
var el_w= $(this.el).innerWidth();
var el_h= $(this.el).innerHeight();
var xscale= viewport_w / el_w;
var yscale= viewport_h / el_h;
var ypad= parseInt((viewport_h - el_h)/2)+'px';
// console.log('xscale',xscale,'yscale',yscale,'viewport_w',viewport_w,'el_w',el_w,'viewport_h',viewport_h,'el_h',el_h,'ypad',ypad);
// Example:
// 50x10 inside 100x60, xscale=2, yscale=6, pad h with 40, 20 top 20 bottom
var scale= (xscale < yscale)? xscale : yscale;
$(this.el).css('margin', ypad+' 0').css('transform', 'scale('+scale+','+scale+')');
}
Slide.prototype.top= function() { return $(this.el).offset().top }
Slide.prototype.show= function(show) { show? $(this.el).show() : $(this.el).hide(); return this }
Slide.prototype.hide= function() { return this.show(false) }
function _num_is_in_ranges(num, ranges) {
if (ranges)
for (var i= 0; i < ranges.length; i++)
if (num >= ranges[i][0] && (ranges[i].length == 1 || num <= ranges[i][1]))
return true;
return false;
}
Slide.prototype.showStep= function(step_num, view_mode) {
var self= this;
if (step_num < 0) step_num= this.max_step + 1 + step_num;
if (step_num < 0) step_num= 0;
this.steps.each(function() {
var step= $(this);
// If a step is not visible, behavior depends on whether we are the presenter
// and whether the element is temporary. Non-temporary elements need to remain
// in the document flow so that the layout of the rest doesn't jump around.
// But temporary have to be removed from the layout so that they don't occupy
// space. Meanwhile the presenter gets to see all hidden elements.
if (_num_is_in_ranges(step_num, self.parseSteps(this.dataset.step)))
step.css('visibility','visible').css('position','relative').css('opacity',1);
else {
if (view_mode == 'presenter')
step.css('visibility','visible').css('opacity', .3);
else
step.css('visibility','hidden');
if (step.hasClass('temporary-step'))
step.css('position','absolute');
}
});
this.cur_step= step_num;
}
function escape_html(unsafe) {
return (''+unsafe)
.replaceAll('&', "&")
.replaceAll('<', "<")
.replaceAll('>', ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
window.slides= {
config: {},
slides: [],
cur_slide: null,
roles: {},
sharedState: {},
init: function(config) {
var self= this
if (!config)
config= self.config
if (!config.websocket_url)
config.websocket_url= 'slidelink.io'
if (!config.mode)
config.mode= 'obs';
if (config.code_highlight === undefined && window.hljs)
config.code_highlight= function(el){ window.hljs.highlightElement(el) }
if (config.mode == 'presenter' && (config.key == null || config.key.length == 0))
config.key= null;//TODO: getCookieVal('access_key');
self.config= config
// Generic button and checkbox handlers
this._button_dispatch= function(ev) {
try {
self[this.dataset.method].call(self, ev);
} catch (e) {
console.log('Calling slides.'+this.dataset.method+': ', e);
}
};
this._enter_dispatch= function(ev) {
if (ev.keyCode == 13) // Enter
try {
self[this.dataset.enter_method].call(self, ev);
} catch (e) {
console.log('Calling slides.'+this.dataset.enter_method+': ', e);
}
};
this._ckbox_dispatch= function(ev) {
try {
self[this.dataset.method].call(self, this.checked, ev);
} catch(e) {
console.log('Calling slides.'+this.dataset.method+' : ', e);
}
};
this.slides= this.buildSlidesArray();
this.resizeSlides();
window.addEventListener('resize', function(event) { self.resizeSlides() }, true);
self._build_ui()
this._show_slide(1,1);
// The presenter needs a chance to enter the key before connecting
if (config.mode == 'presenter' && config.key == null) {
self.togglemenu();
self.root.find('.status-actions .login').show();
share/public/slides.js view on Meta::CPAN
this.root.find('.status-actions .lead input').prop('checked', enable);
this._update_status();
},
enableFollow: function(enable, ev) {
if (enable) {
this.following= true;
this.enableLead(false);
this.goToLeaderSlide();
} else {
this.following= false;
}
this.root.find('.status-actions .follow input').prop('checked', enable);
this._update_status();
},
enableNavigate: function(enable, ev) {
if (enable) {
this._build_nav_ui();
this.nav_ui.show();
} else {
if (this.nav_ui)
this.nav_ui.hide();
}
this.root.find('.status-actions .navigate input').prop('checked', enable);
},
enableNotes: function(enable, ev) {
if (enable) {
this._build_presenternotes_ui();
this.presenternotes_ui.show();
} else {
if (this._presenternotes_ui)
this.presenternotes_ui.hide();
}
// re-render current slide
this.showNotes= !!enable;
this._show_slide(this.cur_slide, this.getSlide(this.cur_slide).cur_step);
},
_set_conn_note: function(content, duration) {
var self= this;
if (this._conn_note) {
var prev= this._conn_note
delete this._conn_note
prev.fadeOut(500, function(){ prev.remove() })
}
var next= $(content);
this.root.find('.slides-notify').append(next);
this._conn_note= next;
if (duration)
window.setTimeout(function(){
if (self._conn_note == next) this._conn_note= null
next.fadeOut(500, function(){ next.remove() });
}, duration);
this._update_status();
},
_update_status: function() {
var status= this.root.find('.status');
status.empty();
if (this.ws) {
var server= ''+this.ws.url;
server= server.replace(/^wss?:\/\/([^:\/]+).*/, '$1');
if (this.ws.readyState == 0)
status.append('<li class="connecting">Connecting to <span class="host">'+escape_html(server)+'</span></li>');
else if (this.ws.readyState == 1)
status.append('<li class="connected">Connected to <span class="host">'+escape_html(server)+'</span></li>');
else
status.append('<li class="disconnected">Not connected</li>');
}
if (this.following)
status.append('<li class="follow">Following presenter</li>');
else if (this.leading)
status.append('<li class="broadcast">Broadcasting</li>');
if (this.cur_slide && this.slides && this.slides.length)
status.append('<li class="pos">Slide <b>'+this.cur_slide+'</b> of <b>'+this.slides.length+'</b></li>');
},
_handle_connect: function(event, url, mode) {
this.root.find('.reconnect-btn').hide()
this._set_conn_note('<p>Connected</p>', 1500)
if (this.config.mode == 'obs')
this.enableFollow(true);
},
_handle_disconnect: function(event) {
this.root.find('.reconnect-btn').show()
this._set_conn_note('<p>Lost connection</p>')
delete this.ws;
},
_handle_ws_event: function(event) {
//console.log('ws event: ', event);
if (event.state) {
this.sharedState= event.state;
if (this.following)
this.goToLeaderSlide();
}
if (event.roles) {
this.roles= {};
for (var i=0; i < event.roles.length; i++)
this.roles[event.roles[i]]= 1;
if (this.roles.lead) {
this.enableFollow(false);
this.root.find('.status-actions .follow').show();
this.root.find('.status-actions .lead').show();
this.root.find('.status-actions .notes').show();
} else {
// initial state for non-lead is to follow
this.enableFollow(true);
}
if (this.roles.navigate || this.roles.lead) {
this.root.find('.status-actions .navigate').show();
}
}
if (event.key_incorrect) {
// TODO: create a password entry form on the sidebar and a login sequence
// that happens over the websocket.
this._set_conn_note('<p>Incorrect Key</p>');
this.config.key= null;
this.root.find('.status-actions > .login').show();
this.root.find('.reconnect-btn').show()
}
if (event.page_changed) {
window.location.reload();
}
if (event.slides_changed) {
console.log('slides_changed', event.slides_changed);
for (var i=0; i < event.slides_changed.length; i++)
this.replaceSlide(event.slides_changed[i].idx+1, $(event.slides_changed[i].html)[0]);
( run in 2.044 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )