Beautifully crafted timelines that are easy and intuitive to use. http://timeline.knightlab.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
698 lines
18 KiB
698 lines
18 KiB
// VMM.Timeline.js |
|
/* * CodeKit Import |
|
* http://incident57.com/codekit/ |
|
================================================== */ |
|
// @codekit-prepend "Core/VMM.StoryJS.js"; |
|
|
|
// @codekit-append "VMM.Timeline.TimeNav.js"; |
|
// @codekit-append "VMM.Timeline.DataObj.js"; |
|
|
|
|
|
/* Timeline |
|
================================================== */ |
|
|
|
if(typeof VMM != 'undefined' && typeof VMM.Timeline == 'undefined') { |
|
|
|
VMM.Timeline = function(_timeline_id, w, h) { |
|
|
|
var $timeline, |
|
$container, |
|
$feature, |
|
$feedback, |
|
$slider, |
|
$navigation, |
|
slider, |
|
timenav, |
|
version = "2.x", |
|
timeline_id = "#timelinejs", |
|
events = {}, |
|
data = {}, |
|
_dates = [], |
|
config = {}, |
|
has_width = false, |
|
has_height = false, |
|
ie7 = false, |
|
is_moving = false; |
|
|
|
|
|
if (type.of(_timeline_id) == "string") { |
|
if (_timeline_id.match("#")) { |
|
timeline_id = _timeline_id; |
|
} else { |
|
timeline_id = "#" + _timeline_id; |
|
} |
|
} else { |
|
timeline_id = "#timelinejs"; |
|
} |
|
|
|
|
|
/* CONFIG |
|
================================================== */ |
|
config = { |
|
embed: false, |
|
events: { |
|
data_ready: "DATAREADY", |
|
messege: "MESSEGE", |
|
headline: "HEADLINE", |
|
slide_change: "SLIDE_CHANGE", |
|
resize: "resize" |
|
}, |
|
id: timeline_id, |
|
source: "nothing", |
|
type: "timeline", |
|
touch: false, |
|
orientation: "normal", |
|
maptype: "", |
|
version: "2.x", |
|
preload: 4, |
|
current_slide: 0, |
|
hash_bookmark: false, |
|
start_at_end: false, |
|
start_at_slide: 0, |
|
start_zoom_adjust: 0, |
|
start_page: false, |
|
api_keys: { |
|
google: "", |
|
flickr: "", |
|
twitter: "" |
|
}, |
|
interval: 10, |
|
something: 0, |
|
width: 960, |
|
height: 540, |
|
spacing: 15, |
|
loaded: { |
|
slider: false, |
|
timenav: false, |
|
percentloaded: 0 |
|
}, |
|
nav: { |
|
start_page: false, |
|
interval_width: 200, |
|
density: 4, |
|
minor_width: 0, |
|
minor_left: 0, |
|
constraint: { |
|
left: 0, |
|
right: 0, |
|
right_min: 0, |
|
right_max: 0 |
|
}, |
|
zoom: { |
|
adjust: 0 |
|
}, |
|
multiplier: { |
|
current: 6, |
|
min: .1, |
|
max: 50 |
|
}, |
|
rows: [1, 1, 1], |
|
width: 960, |
|
height: 200, |
|
marker: { |
|
width: 150, |
|
height: 50 |
|
} |
|
}, |
|
feature: { |
|
width: 960, |
|
height: 540 |
|
}, |
|
slider: { |
|
width: 720, |
|
height: 400, |
|
content: { |
|
width: 720, |
|
height: 400, |
|
padding: 130, |
|
padding_default:130 |
|
}, |
|
nav: { |
|
width: 100, |
|
height: 200 |
|
} |
|
}, |
|
ease: "easeInOutExpo", |
|
duration: 1000, |
|
gmap_key: "", |
|
language: VMM.Language, |
|
tagSortFunction: function (arr) { |
|
arr.sort(function (a, b) { |
|
return a.localeCompare(b); |
|
}) |
|
} |
|
}; |
|
|
|
if ( w != null && w != "") { |
|
config.width = w; |
|
has_width = true; |
|
} |
|
|
|
if ( h != null && h != "") { |
|
config.height = h; |
|
has_height = true; |
|
} |
|
|
|
if(window.location.hash) { |
|
var hash = window.location.hash.substring(1); |
|
if (!isNaN(hash)) { |
|
config.current_slide = parseInt(hash); |
|
} |
|
} |
|
|
|
window.onhashchange = function () { |
|
var hash = window.location.hash.substring(1); |
|
if (config.hash_bookmark) { |
|
if (is_moving) { |
|
goToEvent(parseInt(hash)); |
|
} else { |
|
is_moving = false; |
|
} |
|
} else { |
|
goToEvent(parseInt(hash)); |
|
} |
|
} |
|
|
|
/* CREATE CONFIG |
|
================================================== */ |
|
function createConfig(conf) { |
|
|
|
// APPLY SUPPLIED CONFIG TO TIMELINE CONFIG |
|
if (typeof embed_config == 'object') { |
|
timeline_config = embed_config; |
|
} |
|
if (typeof timeline_config == 'object') { |
|
trace("HAS TIMELINE CONFIG"); |
|
config = VMM.Util.mergeConfig(config, timeline_config); |
|
} else if (typeof conf == 'object') { |
|
config = VMM.Util.mergeConfig(config, conf); |
|
} |
|
|
|
if (VMM.Browser.device == "mobile" || VMM.Browser.device == "tablet") { |
|
config.touch = true; |
|
} |
|
|
|
config.nav.width = config.width; |
|
config.nav.height = 200; |
|
config.feature.width = config.width; |
|
config.feature.height = config.height - config.nav.height; |
|
config.nav.zoom.adjust = parseInt(config.start_zoom_adjust, 10); |
|
VMM.Timeline.Config = config; |
|
VMM.master_config.Timeline = VMM.Timeline.Config; |
|
this.events = config.events; |
|
|
|
if (config.gmap_key != "") { |
|
config.api_keys.google = config.gmap_key; |
|
} |
|
|
|
trace("VERSION " + config.version); |
|
version = config.version; |
|
} |
|
|
|
/* CREATE TIMELINE STRUCTURE |
|
================================================== */ |
|
function createStructure() { |
|
// CREATE DOM STRUCTURE |
|
$timeline = VMM.getElement(timeline_id); |
|
VMM.Lib.addClass($timeline, "vco-timeline"); |
|
VMM.Lib.addClass($timeline, "vco-storyjs"); |
|
|
|
$container = VMM.appendAndGetElement($timeline, "<div>", "vco-container vco-main"); |
|
$feature = VMM.appendAndGetElement($container, "<div>", "vco-feature"); |
|
$slider = VMM.appendAndGetElement($feature, "<div>", "vco-slider"); |
|
$navigation = VMM.appendAndGetElement($container, "<div>", "vco-navigation"); |
|
$feedback = VMM.appendAndGetElement($timeline, "<div>", "vco-feedback", ""); |
|
|
|
|
|
if (typeof config.language.right_to_left != 'undefined') { |
|
VMM.Lib.addClass($timeline, "vco-right-to-left"); |
|
} |
|
|
|
slider = new VMM.Slider($slider, config); |
|
timenav = new VMM.Timeline.TimeNav($navigation); |
|
|
|
if (!has_width) { |
|
config.width = VMM.Lib.width($timeline); |
|
} else { |
|
VMM.Lib.width($timeline, config.width); |
|
} |
|
|
|
if (!has_height) { |
|
config.height = VMM.Lib.height($timeline); |
|
} else { |
|
VMM.Lib.height($timeline, config.height); |
|
} |
|
|
|
if (config.touch) { |
|
VMM.Lib.addClass($timeline, "vco-touch"); |
|
} else { |
|
VMM.Lib.addClass($timeline, "vco-notouch"); |
|
} |
|
|
|
|
|
} |
|
|
|
/* ON EVENT |
|
================================================== */ |
|
|
|
function onDataReady(e, d) { |
|
trace("onDataReady"); |
|
data = d.timeline; |
|
|
|
if (type.of(data.era) != "array") { |
|
data.era = []; |
|
} |
|
|
|
buildDates(); |
|
|
|
}; |
|
|
|
function reSize() { |
|
|
|
updateSize(); |
|
|
|
slider.setSize(config.feature.width, config.feature.height); |
|
timenav.setSize(config.width, config.height); |
|
if (orientationChange()) { |
|
setViewport(); |
|
} |
|
|
|
}; |
|
|
|
function onSliderLoaded(e) { |
|
config.loaded.slider = true; |
|
onComponentLoaded(); |
|
}; |
|
|
|
function onComponentLoaded(e) { |
|
config.loaded.percentloaded = config.loaded.percentloaded + 25; |
|
|
|
if (config.loaded.slider && config.loaded.timenav) { |
|
hideMessege(); |
|
} |
|
} |
|
|
|
function onTimeNavLoaded(e) { |
|
config.loaded.timenav = true; |
|
onComponentLoaded(); |
|
} |
|
|
|
function onSlideUpdate(e) { |
|
is_moving = true; |
|
config.current_slide = slider.getCurrentNumber(); |
|
setHash(config.current_slide); |
|
timenav.setMarker(config.current_slide, config.ease,config.duration); |
|
}; |
|
|
|
function onMarkerUpdate(e) { |
|
is_moving = true; |
|
config.current_slide = timenav.getCurrentNumber(); |
|
setHash(config.current_slide); |
|
slider.setSlide(config.current_slide); |
|
}; |
|
|
|
function goToEvent(n) { |
|
if (n <= _dates.length - 1 && n >= 0) { |
|
config.current_slide = n; |
|
slider.setSlide(config.current_slide); |
|
timenav.setMarker(config.current_slide, config.ease,config.duration); |
|
} |
|
} |
|
|
|
function setHash(n) { |
|
if (config.hash_bookmark) { |
|
window.location.hash = "#" + n.toString(); |
|
} |
|
} |
|
|
|
function getViewport() { |
|
|
|
} |
|
|
|
function setViewport() { |
|
var viewport_content = "", |
|
viewport_orientation = searchOrientation(window.orientation); |
|
|
|
if (VMM.Browser.device == "mobile") { |
|
if (viewport_orientation == "portrait") { |
|
//viewport_content = "width=device-width; initial-scale=0.75, maximum-scale=0.75"; |
|
viewport_content = "width=device-width; initial-scale=0.5, maximum-scale=0.5"; |
|
} else if (viewport_orientation == "landscape") { |
|
viewport_content = "width=device-width; initial-scale=0.5, maximum-scale=0.5"; |
|
} else { |
|
viewport_content = "width=device-width, initial-scale=1, maximum-scale=1.0"; |
|
} |
|
} else if (VMM.Browser.device == "tablet") { |
|
//viewport_content = "width=device-width, initial-scale=1, maximum-scale=1.0"; |
|
} |
|
|
|
if (document.getElementById("viewport")) { |
|
//VMM.Lib.attr("#viewport", "content", viewport_content); |
|
} else { |
|
//VMM.appendElement("head", "<meta id='viewport' name='viewport' content=" + viewport_content + "/>"); |
|
} |
|
|
|
} |
|
|
|
/* ORIENTATION |
|
================================================== */ |
|
function searchOrientation(orientation) { |
|
var orient = ""; |
|
|
|
if ( orientation == 0 || orientation == 180) { |
|
orient = "portrait"; |
|
} else if ( orientation == 90 || orientation == -90) { |
|
orient = "landscape"; |
|
} else { |
|
orient = "normal"; |
|
} |
|
|
|
return orient; |
|
} |
|
|
|
function orientationChange() { |
|
var orientation = searchOrientation(window.orientation); |
|
|
|
if (orientation == config.orientation) { |
|
return false; |
|
} else { |
|
config.orientation = orientation; |
|
return true; |
|
} |
|
|
|
} |
|
|
|
|
|
/* PUBLIC FUNCTIONS |
|
================================================== */ |
|
this.init = function(c, _data) { |
|
trace('INIT'); |
|
setViewport(); |
|
createConfig(c); |
|
createStructure(); |
|
|
|
if (type.of(_data) == "string") { |
|
config.source = _data; |
|
} |
|
|
|
// LANGUAGE |
|
VMM.Date.setLanguage(config.language); |
|
VMM.master_config.language = config.language; |
|
|
|
// EXTERNAL API |
|
VMM.ExternalAPI.setKeys(config.api_keys); |
|
VMM.ExternalAPI.googlemaps.setMapType(config.maptype); |
|
|
|
// EVENTS |
|
VMM.bindEvent(global, onDataReady, config.events.data_ready); |
|
VMM.bindEvent(global, showMessege, config.events.messege); |
|
|
|
VMM.fireEvent(global, config.events.messege, config.language.messages.loading_timeline); |
|
|
|
/* GET DATA |
|
================================================== */ |
|
if (VMM.Browser.browser == "Explorer" || VMM.Browser.browser == "MSIE") { |
|
if (parseInt(VMM.Browser.version, 10) <= 7 && (VMM.Browser.tridentVersion == null || VMM.Browser.tridentVersion < 4)) { |
|
ie7 = true; |
|
} |
|
} |
|
|
|
if (type.of(config.source) == "string" || type.of(config.source) == "object") { |
|
VMM.Timeline.DataObj.getData(config.source); |
|
} else { |
|
VMM.fireEvent(global, config.events.messege, "No data source provided"); |
|
//VMM.Timeline.DataObj.getData(VMM.getElement(timeline_id)); |
|
} |
|
|
|
|
|
}; |
|
|
|
this.iframeLoaded = function() { |
|
trace("iframeLoaded"); |
|
}; |
|
|
|
this.reload = function(_d) { |
|
trace("Load new timeline data" + _d); |
|
VMM.fireEvent(global, config.events.messege, config.language.messages.loading_timeline); |
|
data = {}; |
|
VMM.Timeline.DataObj.getData(_d); |
|
config.current_slide = 0; |
|
slider.setSlide(0); |
|
timenav.setMarker(0, config.ease,config.duration); |
|
}; |
|
|
|
/* DATA |
|
================================================== */ |
|
function getData(url) { |
|
VMM.getJSON(url, function(d) { |
|
data = VMM.Timeline.DataObj.getData(d); |
|
VMM.fireEvent(global, config.events.data_ready); |
|
}); |
|
}; |
|
|
|
/* MESSEGES |
|
================================================== */ |
|
function showMessege(e, msg, other) { |
|
trace("showMessege " + msg); |
|
//VMM.attachElement($timeline, $feedback); |
|
if (other) { |
|
VMM.attachElement($feedback, msg); |
|
} else{ |
|
VMM.attachElement($feedback, VMM.MediaElement.loadingmessage(msg)); |
|
} |
|
}; |
|
|
|
function hideMessege() { |
|
VMM.Lib.animate($feedback, config.duration, config.ease*4, {"opacity": 0}, detachMessege); |
|
}; |
|
|
|
function detachMessege() { |
|
VMM.Lib.detach($feedback); |
|
} |
|
|
|
/* BUILD DISPLAY |
|
================================================== */ |
|
function build() { |
|
// START AT SLIDE |
|
if (parseInt(config.start_at_slide) > 0 && config.current_slide == 0) { |
|
config.current_slide = parseInt(config.start_at_slide); |
|
} |
|
// START AT END |
|
if (config.start_at_end && config.current_slide == 0) { |
|
config.current_slide = _dates.length - 1; |
|
} |
|
|
|
|
|
// IE7 |
|
if (ie7) { |
|
ie7 = true; |
|
VMM.fireEvent(global, config.events.messege, "Internet Explorer " + VMM.Browser.version + " is not supported by TimelineJS. Please update your browser to version 8 or higher. If you are using a recent version of Internet Explorer you may need to disable compatibility mode in your browser."); |
|
} else { |
|
|
|
detachMessege(); |
|
reSize(); |
|
|
|
// EVENT LISTENERS |
|
VMM.bindEvent($slider, onSliderLoaded, "LOADED"); |
|
VMM.bindEvent($navigation, onTimeNavLoaded, "LOADED"); |
|
VMM.bindEvent($slider, onSlideUpdate, "UPDATE"); |
|
VMM.bindEvent($navigation, onMarkerUpdate, "UPDATE"); |
|
|
|
// INITIALIZE COMPONENTS |
|
slider.init(_dates); |
|
timenav.init(_dates, data.era); |
|
|
|
// RESIZE EVENT LISTENERS |
|
VMM.bindEvent(global, reSize, config.events.resize); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
function updateSize() { |
|
trace("UPDATE SIZE"); |
|
config.width = VMM.Lib.width($timeline); |
|
config.height = VMM.Lib.height($timeline); |
|
|
|
config.nav.width = config.width; |
|
config.feature.width = config.width; |
|
|
|
config.feature.height = config.height - config.nav.height - 3; |
|
|
|
if (VMM.Browser.device == "mobile") { |
|
/* |
|
if (VMM.Browser.orientation == "portrait") { |
|
config.feature.height = 480; |
|
config.height = 480 + config.nav.height; |
|
} else if(VMM.Browser.orientation == "landscape") { |
|
config.feature.height = 320; |
|
config.height = 320 + config.nav.height; |
|
} else { |
|
config.feature.height = config.height - config.nav.height - 3; |
|
} |
|
*/ |
|
} |
|
|
|
if (config.width < 641) { |
|
VMM.Lib.addClass($timeline, "vco-skinny"); |
|
} else { |
|
VMM.Lib.removeClass($timeline, "vco-skinny"); |
|
} |
|
|
|
}; |
|
|
|
// BUILD DATE OBJECTS |
|
// called from onDataReady, passes to function build |
|
function buildDates() { |
|
|
|
_dates = []; |
|
VMM.fireEvent(global, config.events.messege, "Building Dates"); |
|
updateSize(); |
|
|
|
for(var i = 0; i < data.date.length; i++) { |
|
|
|
if (data.date[i].startDate != null && data.date[i].startDate != "") { |
|
|
|
var _date = {}, |
|
do_start = VMM.Date.parse(data.date[i].startDate, true), |
|
do_end; |
|
|
|
_date.startdate = do_start.date; |
|
_date.precisiondate = do_start.precision; |
|
|
|
if (isNaN(_date.startdate)) { |
|
trace("Failed to parse start date " + data.date[i].startDate); |
|
} else { |
|
|
|
|
|
// END DATE |
|
if (data.date[i].endDate != null && data.date[i].endDate != "") { |
|
_date.enddate = VMM.Date.parse(data.date[i].endDate); |
|
} else { |
|
_date.enddate = _date.startdate; |
|
} |
|
|
|
_date.needs_slug = false; |
|
|
|
if (data.date[i].headline == "") { |
|
if (data.date[i].slug != null && data.date[i].slug != "") { |
|
_date.needs_slug = true; |
|
} |
|
} |
|
|
|
_date.title = data.date[i].headline; |
|
_date.headline = data.date[i].headline; |
|
_date.type = data.date[i].type; |
|
_date.date = VMM.Date.prettyDate(_date.startdate, false, _date.precisiondate); |
|
_date.asset = data.date[i].asset; |
|
_date.fulldate = _date.startdate.getTime(); |
|
_date.text = data.date[i].text; |
|
_date.content = ""; |
|
_date.tag = data.date[i].tag; |
|
_date.slug = data.date[i].slug; |
|
_date.uniqueid = VMM.Util.unique_ID(7); |
|
_date.classname = data.date[i].classname; |
|
|
|
|
|
_dates.push(_date); |
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
if (data.date.length != _dates.length) { |
|
showMessege(null,"Error processing data. Check for invalid date formats.") |
|
return; |
|
} |
|
|
|
/* CUSTOM SORT |
|
================================================== */ |
|
if (data.type != "storify") { |
|
_dates.sort(function(a, b){ |
|
return a.fulldate - b.fulldate |
|
}); |
|
} |
|
|
|
/* CREATE START PAGE IF AVAILABLE |
|
================================================== */ |
|
if (data.headline != null && data.headline != "" && data.text != null && data.text != "") { |
|
|
|
var startpage_date, |
|
do_start, |
|
_date = {}, |
|
td_num = 0, |
|
td; |
|
|
|
if (typeof data.startDate != 'undefined') { |
|
do_start = VMM.Date.parse(data.startDate, true); |
|
startpage_date = do_start.date; |
|
} else { |
|
startpage_date = false; |
|
} |
|
trace("HAS STARTPAGE"); |
|
trace(startpage_date); |
|
|
|
if (startpage_date && startpage_date < _dates[0].startdate) { |
|
_date.startdate = new Date(startpage_date); |
|
} else { |
|
td = _dates[0].startdate; |
|
_date.startdate = new Date(_dates[0].startdate); |
|
|
|
if (td.getMonth() === 0 && td.getDate() == 1 && td.getHours() === 0 && td.getMinutes() === 0 ) { |
|
// trace("YEAR ONLY"); |
|
_date.startdate.setFullYear(td.getFullYear() - 1); |
|
} else if (td.getDate() <= 1 && td.getHours() === 0 && td.getMinutes() === 0) { |
|
// trace("YEAR MONTH"); |
|
_date.startdate.setMonth(td.getMonth() - 1); |
|
} else if (td.getHours() === 0 && td.getMinutes() === 0) { |
|
// trace("YEAR MONTH DAY"); |
|
_date.startdate.setDate(td.getDate() - 1); |
|
} else if (td.getMinutes() === 0) { |
|
// trace("YEAR MONTH DAY HOUR"); |
|
_date.startdate.setHours(td.getHours() - 1); |
|
} else { |
|
// trace("YEAR MONTH DAY HOUR MINUTE"); |
|
_date.startdate.setMinutes(td.getMinutes() - 1); |
|
} |
|
} |
|
|
|
_date.uniqueid = VMM.Util.unique_ID(7); |
|
_date.enddate = _date.startdate; |
|
_date.precisiondate = do_start.precision; |
|
_date.title = data.headline; |
|
_date.headline = data.headline; |
|
_date.text = data.text; |
|
_date.type = "start"; |
|
_date.date = VMM.Date.prettyDate(data.startDate, false, _date.precisiondate); |
|
_date.asset = data.asset; |
|
_date.slug = false; |
|
_date.needs_slug = false; |
|
_date.fulldate = _date.startdate.getTime(); |
|
|
|
if (config.embed) { |
|
VMM.fireEvent(global, config.events.headline, _date.headline); |
|
} |
|
|
|
_dates.unshift(_date); |
|
} |
|
|
|
/* CUSTOM SORT |
|
================================================== */ |
|
if (data.type != "storify") { |
|
_dates.sort(function(a, b){ |
|
return a.fulldate - b.fulldate |
|
}); |
|
} |
|
|
|
build(); |
|
} |
|
|
|
}; |
|
|
|
VMM.Timeline.Config = {}; |
|
|
|
};
|
|
|