<commit_before>/*!
 * jQuery Mobile
 * http://jquerymobile.com/
 *
 * Copyright 2010, jQuery Project
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
(function( jQuery, window, undefined ) {
	// if we're missing support for any of these, then we're a C-grade browser
	if ( !jQuery.support.display || !jQuery.support.position || !jQuery.support.overflow || !jQuery.support.floatclear ) {
		return;
	}

	//these properties should be made easy to override externally
	jQuery.mobile = {};

	jQuery.extend(jQuery.mobile, {
		subPageUrlKey: 'ui-page' //define the key used in urls for sub-pages. Defaults to &ui-page=
	});

	var $window = jQuery(window),
		$html = jQuery('html'),
		$head = jQuery('head'),
		$body,
		$loader = jQuery('

loading.

'),
		startPage,
		startPageId = 'ui-page-start',
		activePageClass = 'ui-page-active',
		pageTransition,
		transitions = 'slide slideup slidedown pop flip fade dissolve swap',
		transitionDuration = 350,
		backBtnText = "Back",
		urlStack = [ {
			url: location.hash.replace( /^#/, "" ),
			transition: "slide"
		} ],
		nextPageRole = null,
		noCache = '.ui-dialog';

	// hide address bar
	function hideBrowserChrome() {
		// prevent scrollstart and scrollstop events
		jQuery.event.special.scrollstart.enabled = false;
		setTimeout(function() {
			window.scrollTo( 0, 0 );
		},0);
		setTimeout(function() {
			jQuery.event.special.scrollstart.enabled = true;
		}, 150 );
	}

	function getBaseURL(){
	    var newBaseURL = location.hash.replace(/#/,'').split('/');
		if(newBaseURL.length && /[.|&]/.test(newBaseURL[newBaseURL.length-1]) ){
			newBaseURL.pop();
		}
		newBaseURL = newBaseURL.join('/');
		if(newBaseURL !== "" && newBaseURL.charAt(newBaseURL.length-1) !== '/'){  newBaseURL += '/'; }
		return newBaseURL;
	}

	function setBaseURL(){
		//set base url for new page assets
		$('#ui-base').attr('href', getBaseURL());
	}

	function resetBaseURL(){
		$('#ui-base').attr('href', location.pathname);
	}

	// send a link through hash tracking
	jQuery.fn.ajaxClick = function() {
		var href = jQuery( this ).attr( "href" );
		pageTransition = jQuery( this ).attr( "data-transition" ) || "slide";
		nextPageRole = jQuery( this ).attr( "data-rel" );

		//find new base for url building
		var newBaseURL = getBaseURL();

		//if href is absolute but local, or a local ID, no base needed
		if( /^\//.test(href) || (/https?:\/\//.test(href) && !!(href).match(location.hostname)) || /^#/.test(href) ){
			newBaseURL = '';
		}

		// set href to relative path using baseURL and
		if( !/https?:\/\//.test(href) ){
			href = newBaseURL + href;
		}

		//if it's a non-local-anchor and Ajax is not supported, or if it's an external link, go to page without ajax
		if ( ( /^[^#]/.test(href) && !jQuery.support.ajax ) || ( /https?:\/\//.test(href) && !!!href.match(location.hostname) ) ) {
			location = href
		}
		else{
			// let the hashchange event handler take care of requesting the page via ajax
			location.hash = href;
		}
		return this;
	};

	// ajaxify all navigable links
	jQuery( "a:not([href=#]):not([target]):not([rel=external])" ).live( "click", function(event) {
		jQuery( this ).ajaxClick();
		return false;
	});

	// turn on/off page loading message.
	function pageLoading( done ) {
		if ( done ) {
			$html.removeClass( "ui-loading" );
		} else {
			$loader.appendTo('body');
			$html.addClass( "ui-loading" );
		}
	};

	// transition between pages - based on transitions from jQtouch
	function changePage( from, to, transition, back ) {
		jQuery( document.activeElement ).blur();

		to.appendTo($body);

		//trigger before show/hide events
		from.trigger("beforepagehide", {nextPage: to});
		to.trigger("beforepageshow", {prevPage: from});

		// animate in / out
		from.addClass( transition + " out " + ( back ? "reverse" : "" ) );
		to.addClass( activePageClass + " " + transition +
			" in " + ( back ? "reverse" : "" ) );

		// callback - remove classes, etc
		to.animationComplete(function() {
			from.add( to ).removeClass(" out in reverse " + transitions );
			from.removeClass( activePageClass );

			//trigger show/hide events
			from.trigger("pagehide", {nextPage: to});
			to.trigger("pageshow", {prevPage: from});

			pageLoading( true );
		});
	};

	jQuery(function() {
		$body = jQuery( "body" );
		pageLoading();

		// needs to be bound at domready (for IE6)
		// find or load content, make it active
		$window.bind( "hashchange", function(e) {
			var url = location.hash.replace( /^#/, "" ),
				stackLength = urlStack.length,
				// pageTransition only exists if the user clicked a link
				back = !pageTransition && stackLength > 1 &&
					urlStack[ stackLength - 2 ].url === url,
				transition = pageTransition || "slide",
				fileUrl = url;
			pageTransition = undefined;

			//reset base to pathname for new request
			resetBaseURL();

			// if the new href is the same as the previous one
			if ( back ) {
				transition = urlStack.pop().transition;
			} else {
				urlStack.push({ url: url, transition: transition });
			}

			//remove any pages that shouldn't cache
			jQuery(noCache).remove();

			//function for setting role of next page
			function setPageRole( newPage ) {
				if ( nextPageRole ) {
					newPage.attr( "data-role", nextPageRole );
					nextPageRole = undefined;
				}
			}

			if ( url ) {
				// see if content is present already
				var localDiv = jQuery( "[id='" + url + "']" );
				if ( localDiv.length ) {
					if ( localDiv.is( "[data-role]" ) ) {
						setPageRole( localDiv );
					}
					setBaseURL();
					mobilize( localDiv );
					changePage( jQuery( ".ui-page-active" ), localDiv, transition, back );
				} else { //ajax it in
					pageLoading();
					if(url.match( '&' + jQuery.mobile.subPageUrlKey )){
						fileUrl = url.split( '&' + jQuery.mobile.subPageUrlKey )[0];
					}
					var newPage = jQuery( "
" )
						.appendTo( $body )
						.load( fileUrl + ' [data-role="page"]', function() {
							// TODO: test this (avoids querying the dom for new element):
//							var newPage = jQuery( this ).find( ".ui-page" ).eq( 0 )
//								.attr( "id", url );
//							jQuery( this ).replaceWith( newPage );
//							setPageRole( newPage );
//							mobilize( newPage );
//							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
							jQuery( this ).replaceWith(
								jQuery( this ).find( '[data-role="page"]' ).eq( 0 ).attr( "id", fileUrl ) );
							var newPage = jQuery( "[id='" + fileUrl + "']" );
							setPageRole( newPage );
							mobilize( newPage );
							newPage = jQuery( "[id='" + url + "']" );
							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
						});

						setBaseURL();
				}
			} else {
				// either we've backed up to the root page url
				// or it's the first page load with no hash present
				var currentPage = jQuery( ".ui-page-active" );
				if ( currentPage.length && !startPage.is( ".ui-page-active" ) ) {
					changePage( currentPage, startPage, transition, back );
				} else {
					startPage.trigger("beforepageshow", {prevPage: $('')});
					startPage.addClass( activePageClass );
					//FIXME: when there's no prevPage, is passing an empty jQuery obj proper style?
					startPage.trigger("pageshow", {prevPage: $('')});
					pageLoading( true );
				}
			}
		});
	});

	//add orientation class on flip/resize.
	$window.bind( "orientationchange", function( event, data ) {
		$html.removeClass( "portrait landscape" ).addClass( data.orientation );
	});

	//add mobile, loading classes to doc
	$html.addClass('ui-mobile');

	//insert mobile meta - these will need to be configurable somehow.
	$head.append('<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0" />'+
		'<link rel="apple-touch-icon" href="images/homeicon.png" />'+
		'<link rel="apple-touch-startup-image" href="images/startscreen.png" />'+
		'<meta name="apple-mobile-web-app-capable" content="yes" />' +
        '<meta name="apple-mobile-web-app-status-bar-style" content="default" />'+
        '<base  href="" id="ui-base" />');

    //set base href to pathname
    resetBaseURL();

	//potential (probably incomplete) fallback to workaround lack of animation callbacks.
	//should this be extended into a full special event?
	// note: Expects CSS animations use transitionDuration (350ms)
	jQuery.fn.animationComplete = function(callback){
		if(jQuery.support.WebKitAnimationEvent){
			return jQuery(this).one('webkitAnimationEnd', callback); //check out transitionEnd (opera per Paul's request)
		}
		else{
			setTimeout(callback, transitionDuration);
		}
	};

	//markup-driven enhancements, to be called on any ui-page upon loading
	function mobilize($el){
		//to-do: make sure this only runs one time on a page (or maybe per component)
		return $el.not('[data-mobilized]').each(function(){

			var $el = $(this);

			$el.trigger("beforeload");

			//some of the form elements currently rely on the presence of ui-page and ui-content
			// classes so we'll handle page and content roles outside of the main role processing
			// loop below.
			$el.find('[data-role=page],[data-role=content]').andSelf().each(function() {
					var $this = $(this);
					$this.addClass('ui-' + $this.attr("data-role"));
			});

			//hide no-js content
			$el.find('[data-role="nojs"]').addClass('ui-nojs');

			//replace HTML5 input types that have crap browser implementations
			$el.find('input').not('[type=text],[type=submit],[type=reset],[type=image],[type=button]').each(function(){
				$(this).replaceWith( $( '
' ).html( $(this).clone() ).html().replace(/type="([a-zA-Z]+)"/, 'data-type="$1"') );
			});

			$el.find('input[type=radio],input[type=checkbox]').customCheckboxRadio();
			$el.find('button, input[type=submit], input[type=reset], input[type=image]').not('.ui-nojs').customButton();
			$el.find('input[type=text],input[type=password],textarea').customTextInput();
			$el.find("input, select").filter('[data-role="slider"]').slider();
			$el.find('select').not('[data-role="slider"]').customSelect();

			//pre-find data els
			var $dataEls = $el.find('[data-role]').andSelf().each(function() {
				var $this = $(this),
					role = $this.attr("data-role");
				switch(role) {
				case 'header':
				case 'footer':
					$this.addClass('ui-bar-' + ($(this).data('theme') ? $(this).data('theme') : 'a'));
				case 'page':
				case 'content':
					$this.addClass('ui-' + role);
					break;
				case 'collapsible':
				case 'fieldcontain':
				case 'navlist':
				case 'listview':
				case 'dialog':
				case 'ajaxform':
					$this[role]();
					break;
				case 'controlgroup':
					// FIXME for some reason this has to come before the form control stuff (see above)
					$this.controlgroup({
						direction: $this.attr("data-type")
					});
					break;
				}
			});

			//fix toolbars
			$el.fixHeaderFooter();

			//add back buttons to headers that don't have them
			// FIXME make that optional?
			// TODO don't do that on devices that have a native back button?
			var backBtn = $el.find('.ui-header:not(.ui-listbox-header) a.ui-back');
			if(!backBtn.length){
				backBtn = jQuery('').appendTo($el.find('.ui-header:not(.ui-listbox-header)'));
			}

			//buttons from links in headers,footers,bars, or with data-role
			$dataEls.filter('[data-role="button"]').add('.ui-header a, .ui-footer a, .ui-bar a').not('.ui-btn').buttonMarkup();
			//links within content areas
			$el.find('.ui-body a:not(.ui-btn):not(.ui-link-inherit)').addClass('ui-link');
			//make all back buttons mimic the back button (pre-js, these links are usually "home" links)
			backBtn
				.click(function(){
					// FIXME if you navigated from some page directly to subpage (ala .../#_form-controls.html), back button will actually go to the previous page, instead of the parent page
					history.go(-1);
					return false;
				})
				.find('.ui-btn-text').text(backBtnText);

			$el.attr('data-mobilized', true);
			$el.trigger("load");
		});
	};

	jQuery.extend({
		mobilize: mobilize,
		pageLoading: pageLoading,
		changePage: changePage,
		hideBrowserChrome: hideBrowserChrome
	});

	//dom-ready
	jQuery(function(){

		//set up active page - mobilize it!
		startPage = jQuery('[data-role="page"]:first');

		//make sure it has an ID - for finding it later
		if(!startPage.attr('id')){
			startPage.attr('id', startPageId);
		}

		//mobilize all pages present
		mobilize(jQuery('[data-role="page"]'));

		//trigger a new hashchange, hash or not
		$window.trigger( "hashchange" );

		//update orientation
		$html.addClass( jQuery.event.special.orientationchange.orientation( $window ) );

		//some debug stuff for the events pages
		jQuery('body').bind('scrollstart scrollstop swipe swipeleft swiperight tap taphold turn',function(e){
			jQuery('#eventlogger').prepend('
Event fired: '+ e.type +'
');
		});
	});

	$window.load(hideBrowserChrome);

})( jQuery, this );
<commit_msg>added radio and checks to the input type blacklist and added an option $.mobilize to degradeHTML5inputs or not<commit_after>/*!
 * jQuery Mobile
 * http://jquerymobile.com/
 *
 * Copyright 2010, jQuery Project
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
(function( jQuery, window, undefined ) {
	// if we're missing support for any of these, then we're a C-grade browser
	if ( !jQuery.support.display || !jQuery.support.position || !jQuery.support.overflow || !jQuery.support.floatclear ) {
		return;
	}

	//these properties should be made easy to override externally
	jQuery.mobile = {};

	jQuery.extend(jQuery.mobile, {
		subPageUrlKey: 'ui-page', //define the key used in urls for sub-pages. Defaults to &ui-page=
		degradeHTML5inputs: true
	});

	var $window = jQuery(window),
		$html = jQuery('html'),
		$head = jQuery('head'),
		$body,
		$loader = jQuery('

loading.

'),
		startPage,
		startPageId = 'ui-page-start',
		activePageClass = 'ui-page-active',
		pageTransition,
		transitions = 'slide slideup slidedown pop flip fade dissolve swap',
		transitionDuration = 350,
		backBtnText = "Back",
		urlStack = [ {
			url: location.hash.replace( /^#/, "" ),
			transition: "slide"
		} ],
		nextPageRole = null,
		noCache = '.ui-dialog';

	// hide address bar
	function hideBrowserChrome() {
		// prevent scrollstart and scrollstop events
		jQuery.event.special.scrollstart.enabled = false;
		setTimeout(function() {
			window.scrollTo( 0, 0 );
		},0);
		setTimeout(function() {
			jQuery.event.special.scrollstart.enabled = true;
		}, 150 );
	}

	function getBaseURL(){
	    var newBaseURL = location.hash.replace(/#/,'').split('/');
		if(newBaseURL.length && /[.|&]/.test(newBaseURL[newBaseURL.length-1]) ){
			newBaseURL.pop();
		}
		newBaseURL = newBaseURL.join('/');
		if(newBaseURL !== "" && newBaseURL.charAt(newBaseURL.length-1) !== '/'){  newBaseURL += '/'; }
		return newBaseURL;
	}

	function setBaseURL(){
		//set base url for new page assets
		$('#ui-base').attr('href', getBaseURL());
	}

	function resetBaseURL(){
		$('#ui-base').attr('href', location.pathname);
	}

	// send a link through hash tracking
	jQuery.fn.ajaxClick = function() {
		var href = jQuery( this ).attr( "href" );
		pageTransition = jQuery( this ).attr( "data-transition" ) || "slide";
		nextPageRole = jQuery( this ).attr( "data-rel" );

		//find new base for url building
		var newBaseURL = getBaseURL();

		//if href is absolute but local, or a local ID, no base needed
		if( /^\//.test(href) || (/https?:\/\//.test(href) && !!(href).match(location.hostname)) || /^#/.test(href) ){
			newBaseURL = '';
		}

		// set href to relative path using baseURL and
		if( !/https?:\/\//.test(href) ){
			href = newBaseURL + href;
		}

		//if it's a non-local-anchor and Ajax is not supported, or if it's an external link, go to page without ajax
		if ( ( /^[^#]/.test(href) && !jQuery.support.ajax ) || ( /https?:\/\//.test(href) && !!!href.match(location.hostname) ) ) {
			location = href
		}
		else{
			// let the hashchange event handler take care of requesting the page via ajax
			location.hash = href;
		}
		return this;
	};

	// ajaxify all navigable links
	jQuery( "a:not([href=#]):not([target]):not([rel=external])" ).live( "click", function(event) {
		jQuery( this ).ajaxClick();
		return false;
	});

	// turn on/off page loading message.
	function pageLoading( done ) {
		if ( done ) {
			$html.removeClass( "ui-loading" );
		} else {
			$loader.appendTo('body');
			$html.addClass( "ui-loading" );
		}
	};

	// transition between pages - based on transitions from jQtouch
	function changePage( from, to, transition, back ) {
		jQuery( document.activeElement ).blur();

		to.appendTo($body);

		//trigger before show/hide events
		from.trigger("beforepagehide", {nextPage: to});
		to.trigger("beforepageshow", {prevPage: from});

		// animate in / out
		from.addClass( transition + " out " + ( back ? "reverse" : "" ) );
		to.addClass( activePageClass + " " + transition +
			" in " + ( back ? "reverse" : "" ) );

		// callback - remove classes, etc
		to.animationComplete(function() {
			from.add( to ).removeClass(" out in reverse " + transitions );
			from.removeClass( activePageClass );

			//trigger show/hide events
			from.trigger("pagehide", {nextPage: to});
			to.trigger("pageshow", {prevPage: from});

			pageLoading( true );
		});
	};

	jQuery(function() {
		$body = jQuery( "body" );
		pageLoading();

		// needs to be bound at domready (for IE6)
		// find or load content, make it active
		$window.bind( "hashchange", function(e) {
			var url = location.hash.replace( /^#/, "" ),
				stackLength = urlStack.length,
				// pageTransition only exists if the user clicked a link
				back = !pageTransition && stackLength > 1 &&
					urlStack[ stackLength - 2 ].url === url,
				transition = pageTransition || "slide",
				fileUrl = url;
			pageTransition = undefined;

			//reset base to pathname for new request
			resetBaseURL();

			// if the new href is the same as the previous one
			if ( back ) {
				transition = urlStack.pop().transition;
			} else {
				urlStack.push({ url: url, transition: transition });
			}

			//remove any pages that shouldn't cache
			jQuery(noCache).remove();

			//function for setting role of next page
			function setPageRole( newPage ) {
				if ( nextPageRole ) {
					newPage.attr( "data-role", nextPageRole );
					nextPageRole = undefined;
				}
			}

			if ( url ) {
				// see if content is present already
				var localDiv = jQuery( "[id='" + url + "']" );
				if ( localDiv.length ) {
					if ( localDiv.is( "[data-role]" ) ) {
						setPageRole( localDiv );
					}
					setBaseURL();
					mobilize( localDiv );
					changePage( jQuery( ".ui-page-active" ), localDiv, transition, back );
				} else { //ajax it in
					pageLoading();
					if(url.match( '&' + jQuery.mobile.subPageUrlKey )){
						fileUrl = url.split( '&' + jQuery.mobile.subPageUrlKey )[0];
					}
					var newPage = jQuery( "
" )
						.appendTo( $body )
						.load( fileUrl + ' [data-role="page"]', function() {
							// TODO: test this (avoids querying the dom for new element):
//							var newPage = jQuery( this ).find( ".ui-page" ).eq( 0 )
//								.attr( "id", url );
//							jQuery( this ).replaceWith( newPage );
//							setPageRole( newPage );
//							mobilize( newPage );
//							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
							jQuery( this ).replaceWith(
								jQuery( this ).find( '[data-role="page"]' ).eq( 0 ).attr( "id", fileUrl ) );
							var newPage = jQuery( "[id='" + fileUrl + "']" );
							setPageRole( newPage );
							mobilize( newPage );
							newPage = jQuery( "[id='" + url + "']" );
							changePage( jQuery( ".ui-page-active" ), newPage, transition, back );
						});

						setBaseURL();
				}
			} else {
				// either we've backed up to the root page url
				// or it's the first page load with no hash present
				var currentPage = jQuery( ".ui-page-active" );
				if ( currentPage.length && !startPage.is( ".ui-page-active" ) ) {
					changePage( currentPage, startPage, transition, back );
				} else {
					startPage.trigger("beforepageshow", {prevPage: $('')});
					startPage.addClass( activePageClass );
					//FIXME: when there's no prevPage, is passing an empty jQuery obj proper style?
					startPage.trigger("pageshow", {prevPage: $('')});
					pageLoading( true );
				}
			}
		});
	});

	//add orientation class on flip/resize.
	$window.bind( "orientationchange", function( event, data ) {
		$html.removeClass( "portrait landscape" ).addClass( data.orientation );
	});

	//add mobile, loading classes to doc
	$html.addClass('ui-mobile');

	//insert mobile meta - these will need to be configurable somehow.
	$head.append('<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0" />'+
		'<link rel="apple-touch-icon" href="images/homeicon.png" />'+
		'<link rel="apple-touch-startup-image" href="images/startscreen.png" />'+
		'<meta name="apple-mobile-web-app-capable" content="yes" />' +
        '<meta name="apple-mobile-web-app-status-bar-style" content="default" />'+
        '<base  href="" id="ui-base" />');

    //set base href to pathname
    resetBaseURL();

	//potential (probably incomplete) fallback to workaround lack of animation callbacks.
	//should this be extended into a full special event?
	// note: Expects CSS animations use transitionDuration (350ms)
	jQuery.fn.animationComplete = function(callback){
		if(jQuery.support.WebKitAnimationEvent){
			return jQuery(this).one('webkitAnimationEnd', callback); //check out transitionEnd (opera per Paul's request)
		}
		else{
			setTimeout(callback, transitionDuration);
		}
	};

	//markup-driven enhancements, to be called on any ui-page upon loading
	function mobilize($el){
		//to-do: make sure this only runs one time on a page (or maybe per component)
		return $el.not('[data-mobilized]').each(function(){

			var $el = $(this);

			$el.trigger("beforeload");

			//some of the form elements currently rely on the presence of ui-page and ui-content
			// classes so we'll handle page and content roles outside of the main role processing
			// loop below.
			$el.find('[data-role=page],[data-role=content]').andSelf().each(function() {
					var $this = $(this);
					$this.addClass('ui-' + $this.attr("data-role"));
			});

			//hide no-js content
			$el.find('[data-role="nojs"]').addClass('ui-nojs');

			//replace HTML5 input types that have crap browser implementations
			if($.mobile.degradeHTML5inputs){
				$el.find('input').not('[type=text],[type=submit],[type=reset],[type=image],[type=button],[type=checkbox],[type=radio]').each(function(){
					$(this).replaceWith( $( '
' ).html( $(this).clone() ).html().replace(/type="([a-zA-Z]+)"/, 'data-type="$1"') );
				});
			}

			$el.find('input[type=radio],input[type=checkbox]').customCheckboxRadio();
			$el.find('button, input[type=submit], input[type=reset], input[type=image],[type=button]').not('.ui-nojs').customButton();
			$el.find('input, textarea').not('[type=submit],[type=reset],[type=image],[type=button],[type=checkbox],[type=radio]').customTextInput();
			$el.find("input, select").filter('[data-role="slider"]').slider();
			$el.find('select').not('[data-role="slider"]').customSelect();

			//pre-find data els
			var $dataEls = $el.find('[data-role]').andSelf().each(function() {
				var $this = $(this),
					role = $this.attr("data-role");
				switch(role) {
				case 'header':
				case 'footer':
					$this.addClass('ui-bar-' + ($(this).data('theme') ? $(this).data('theme') : 'a'));
				case 'page':
				case 'content':
					$this.addClass('ui-' + role);
					break;
				case 'collapsible':
				case 'fieldcontain':
				case 'navlist':
				case 'listview':
				case 'dialog':
				case 'ajaxform':
					$this[role]();
					break;
				case 'controlgroup':
					// FIXME for some reason this has to come before the form control stuff (see above)
					$this.controlgroup({
						direction: $this.attr("data-type")
					});
					break;
				}
			});

			//fix toolbars
			$el.fixHeaderFooter();

			//add back buttons to headers that don't have them
			// FIXME make that optional?
			// TODO don't do that on devices that have a native back button?
			var backBtn = $el.find('.ui-header:not(.ui-listbox-header) a.ui-back');
			if(!backBtn.length){
				backBtn = jQuery('').appendTo($el.find('.ui-header:not(.ui-listbox-header)'));
			}

			//buttons from links in headers,footers,bars, or with data-role
			$dataEls.filter('[data-role="button"]').add('.ui-header a, .ui-footer a, .ui-bar a').not('.ui-btn').buttonMarkup();
			//links within content areas
			$el.find('.ui-body a:not(.ui-btn):not(.ui-link-inherit)').addClass('ui-link');
			//make all back buttons mimic the back button (pre-js, these links are usually "home" links)
			backBtn
				.click(function(){
					// FIXME if you navigated from some page directly to subpage (ala .../#_form-controls.html), back button will actually go to the previous page, instead of the parent page
					history.go(-1);
					return false;
				})
				.find('.ui-btn-text').text(backBtnText);

			$el.attr('data-mobilized', true);
			$el.trigger("load");
		});
	};

	jQuery.extend({
		mobilize: mobilize,
		pageLoading: pageLoading,
		changePage: changePage,
		hideBrowserChrome: hideBrowserChrome
	});

	//dom-ready
	jQuery(function(){

		//set up active page - mobilize it!
		startPage = jQuery('[data-role="page"]:first');

		//make sure it has an ID - for finding it later
		if(!startPage.attr('id')){
			startPage.attr('id', startPageId);
		}

		//mobilize all pages present
		mobilize(jQuery('[data-role="page"]'));

		//trigger a new hashchange, hash or not
		$window.trigger( "hashchange" );

		//update orientation
		$html.addClass( jQuery.event.special.orientationchange.orientation( $window ) );

		//some debug stuff for the events pages
		jQuery('body').bind('scrollstart scrollstop swipe swipeleft swiperight tap taphold turn',function(e){
			jQuery('#eventlogger').prepend('
Event fired: '+ e.type +'
');
		});
	});

	$window.load(hideBrowserChrome);

})( jQuery, this );
<|endoftext|>


<commit_before>/*global define,dojo */
/*jslint browser:true,sloppy:true,nomen:true,unparam:true,plusplus:true */
/*
 | Copyright 2014 Esri
 |
 | Licensed under the Apache License, Version 2.0 (the "License");
 | you may not use this file except in compliance with the License.
 | You may obtain a copy of the License at
 |
 |    http://www.apache.org/licenses/LICENSE-2.0
 |
 | Unless required by applicable law or agreed to in writing, software
 | distributed under the License is distributed on an "AS IS" BASIS,
 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | See the License for the specific language governing permissions and
 | limitations under the License.
 */
//============================================================================================================================//
define([
    "dojo/_base/declare",
    "dojox/gfx",
    "dojox/gfx/utils",
    "dojo/_base/array",
    "dojo/domReady!"
], function (
    declare,
    gfx,
    gfxUtils,
    array
) {

    //========================================================================================================================//

    return {

        /**
         * Creates an SVG item in the specified container.
         * @param {object} definition JSON text describing the SVG item, e.g.,
         * "[{'shape':{'type':'circle','cx':13,'cy':13,'r':10},...,'style':'solid','width':2,'cap':'butt','join':4}}]"
         * Its color(s) for lines and text fill are ignored
         * @param {object} container The div to receive the item
         * @param {number} width The width to use for the item
         * @param {number} height The height to use for the item
         * @return {object} The created SVG surface, which can be used, e.g., to change the
         * color of the SVG; @see {@link changeColor}
         */
        createSVGItem: function (definition, container, width, height) {
            var surface;

            // Create the vectors from the JSON description
            surface = gfx.createSurface(container, width, height);
            gfxUtils.fromJson(surface, definition);

            return surface;
        },

        /**
         * Changes the color of the children of an SVG surface.
         * @param {object} surface An SVG surface
         * @param {object} newColor The new color as "a named color, hex color,
         * linear gradient, or radial gradient"
         * (http://dojotoolkit.org/documentation/tutorials/1.10/gfx/)
         * @example
         * changeColor(surface, "#007ac2");
         * @example
         * changeColor(surface, "red");
         */
        changeColor: function (surface, newColor) {
            array.forEach(surface.children, function (component) {
                var stroke, fill;

                stroke = component.getStroke();
                if (stroke) {
                    stroke.color = newColor;
                    component.setStroke(stroke);
                }

                fill = component.getFill();
                if (fill) {
                    component.setFill(newColor);
                }
            });
        }

    };

    //========================================================================================================================//

});
<commit_msg>Waited until gfx surface is ready before using it<commit_after>/*global define,dojo */
/*jslint browser:true,sloppy:true,nomen:true,unparam:true,plusplus:true */
/*
 | Copyright 2014 Esri
 |
 | Licensed under the Apache License, Version 2.0 (the "License");
 | you may not use this file except in compliance with the License.
 | You may obtain a copy of the License at
 |
 |    http://www.apache.org/licenses/LICENSE-2.0
 |
 | Unless required by applicable law or agreed to in writing, software
 | distributed under the License is distributed on an "AS IS" BASIS,
 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | See the License for the specific language governing permissions and
 | limitations under the License.
 */
//============================================================================================================================//
define([
    "dojo/_base/declare",
    "dojox/gfx",
    "dojox/gfx/utils",
    "dojo/_base/array",
    "dojo/domReady!"
], function (
    declare,
    gfx,
    gfxUtils,
    array
) {

    //========================================================================================================================//

    return {

        /**
         * Creates an SVG item in the specified container.
         * @param {object} definition JSON text describing the SVG item, e.g.,
         * "[{'shape':{'type':'circle','cx':13,'cy':13,'r':10},...,'style':'solid','width':2,'cap':'butt','join':4}}]"
         * Its color(s) for lines and text fill are ignored
         * @param {object} container The div to receive the item
         * @param {number} width The width to use for the item
         * @param {number} height The height to use for the item
         * @return {object} The created SVG surface, which can be used, e.g., to change the
         * color of the SVG; @see {@link changeColor}
         */
        createSVGItem: function (definition, container, width, height) {
            var surface;

            // Create the vectors from the JSON description
            surface = gfx.createSurface(container, width, height);
            surface.whenLoaded(function () {
                // Wait until surface is ready before drawing on it
                // http://dojotoolkit.org/reference-guide/1.10/dojox/gfx.html#core-concepts
                gfxUtils.fromJson(surface, definition);
            });

            return surface;
        },

        /**
         * Changes the color of the children of an SVG surface.
         * @param {object} surface An SVG surface
         * @param {object} newColor The new color as "a named color, hex color,
         * linear gradient, or radial gradient"
         * (http://dojotoolkit.org/documentation/tutorials/1.10/gfx/)
         * @example
         * changeColor(surface, "#007ac2");
         * @example
         * changeColor(surface, "red");
         */
        changeColor: function (surface, newColor) {
            array.forEach(surface.children, function (component) {
                var stroke, fill;

                stroke = component.getStroke();
                if (stroke) {
                    stroke.color = newColor;
                    component.setStroke(stroke);
                }

                fill = component.getFill();
                if (fill) {
                    component.setFill(newColor);
                }
            });
        }

    };

    //========================================================================================================================//

});
<|endoftext|>


<commit_before>import _ from 'lodash';

export const defaults = {
    // HTML node that will become DropZone
    node: null,
    // limit of files within store
    maxFiles: 5,
    // limit total size of files (3e+8 === 300mb)
    maxSize: 3e+8,
    // a whitelist of file types to validate against
    whitelist: [],
    // file enters window
    windowEnter: function () {},
    // file leaves window
    windowLeave: function () {},
    // drop on the window outside of the DropZone
    windowDrop: function () {},
    // drop on the DropZone
    dropZoneDrop: function () {},
    dropZoneEnter: function () {},
    // dragged files left DropZone
    dropZoneLeave: function () {},
    // removed file
    fileRemoved: function () {}
};

export default class DropZone {
    constructor (options, validator) {
        this.validator = validator;
        this.options = _.extend({}, defaults, options);
        // ensure we've got integers here, there is a chance these will come
        // in as strings from a DOM node's attributes
        this.options.maxFiles = parseInt(this.options.maxFiles);
        this.options.maxSize = parseInt(this.options.maxSize);
        // cache methods with context
        this.handleDropWithContext = this.handleDrop.bind(this);
        this.handleWindowEnterWithContext = this.handleWindowEnter.bind(this);
        this.handleWindowLeaveWithContext = this.handleWindowLeave.bind(this);
        this.startIdleTimerWithContext = this.startIdleTimer.bind(this);
        // a place to be used externally as a instance based cache;
        this.data = {};
        this.setup();
    }

    setup () {
        this.node = DropZone.getVanillaNode(window, this.options.node);

        if (!this.node) {
            throw new Error('DropZone requires a DOM node');
        }

        this.eventPool = [];
        this.files = [];
        this.idleTimer = null;
        this.idleDuration = 1500;
        this.eventTracker = { node: {}, window: {} };
        this.size = 0;
        this.windowActive = false;
        this.dropZoneActive = false;

        this.registerEvents();
        this.attachMultipleListeners(
            [window],
            'drag dragstart dragend dragover dragenter dragleave drop',
            this.preventer
        );
    }

    /**
     * Register drag & drop events
     */
    registerEvents () {
        // prevent defaults
        this.addEventAndStore(window, 'drag', this.preventer);
        this.addEventAndStore(window, 'dragstart', this.preventer);
        this.addEventAndStore(window, 'dragend', this.preventer);
        this.addEventAndStore(window, 'dragover', this.preventer);
        this.addEventAndStore(window, 'dragenter', this.preventer);
        this.addEventAndStore(window, 'dragleave', this.preventer);
        this.addEventAndStore(window, 'drop', this.preventer);
        // interactions
        this.addEventAndStore(window, 'dragenter', this.handleWindowEnterWithContext);
        this.addEventAndStore(window, 'dragleave', this.handleWindowLeaveWithContext);
        this.addEventAndStore(window, 'drop', this.handleDropWithContext);
        // attempt to handle missed callbacks
        // mouseout has proven to be _more_ reliable than dragleave
        this.addEventAndStore(document, 'mouseout', this.startIdleTimerWithContext);
    }

    /**
     * Trigger a timer to execute a manual windowLeave after x ms
     * this is to catch window dragLeave events that are missed at high speed
     * @param {Event} event
     */
    startIdleTimer (event) {
        this.clearIdleTimer();
        this.idleTimer = setTimeout(() => {

            if (this.windowActive || this.dropZoneActive) {
                this.handleWindowLeave(event, true);
            }

            this.idleTimer = null;
        }, this.idleDuration);
    }

    /**
     * Clear the current timer
     */
    clearIdleTimer () {
        if (this.idleTimer !== null) {
            clearTimeout(this.idleTimer);
        }
    }

    /**
     * Handle drop event. Checks to see if we can add more files to the files store
     * @param  {Event} event
     */
    handleDrop (event) {
        const files = event.dataTransfer.items;

        // determine where the drop has taken place
        // dropped on the DropZone
        if (this.windowActive && this.dropZoneActive) {
            this.addFiles(files);
        // dropped on the window
        } else if (this.windowActive && !this.dropZoneActive) {
            this.createCallback(this.options.windowDrop, { files: this.files, node: this.node });
        }

        // reset DropZone state§
        this.windowActive = false;
        this.dropZoneActive = false;
        this.resetEventTracker();
    }

    /**
     * Validate and add files to the DropZone
     * @param {FileList} files
     * @param {Object} meta
     */
    addFiles (files, meta = {}) {
        const { valid, text } = this.validator.validate(files, this.getFiles().length, this.getSize());
        const processedFiles = [...files].map(
            file => this.processFile(file.getAsFile ? file.getAsFile() : file, meta)
        );

        if (valid) {
           this.files = [...this.files, ...processedFiles];
        }

        // fire dropped callback
        this.createCallback(this.options.dropZoneDrop, { files: processedFiles, node: this.node, valid, text });
    }

    /**
     * Determine if a file is currently on the window
     */
    fileOnWindow (event) {
        const { clientX, clientY } = event;
        const offWindow = clientX === 0 && clientY === 0;
        return !offWindow && document.elementFromPoint(clientX, clientY);
    }

    /**
     * Handle a file dragged into the window
     */
    handleWindowEnter (event) {
        const onWindow = this.fileOnWindow(event);
        const onDropZone = this.node.contains(onWindow);

        this.clearIdleTimer();

        if (onDropZone && !this.dropZoneActive) {
            this.handleEnter(event.dataTransfer.items);
        } else if (this.fileOnWindow(event) && !this.windowActive) {
            const files = event.dataTransfer.items;
            const { valid, text } = this.validator.validate(files, this.getFiles().length, this.getSize());

            this.createCallback(this.options.windowEnter, { valid, text });
            this.windowActive = true;
        }
    }

    /**
     * Handle a file being dragged off of the window
     * @param {Event} event
     * @param {Boolean} force
     */
    handleWindowLeave (event, force = false) {
        const onDropZone = this.node.contains(document.elementFromPoint(event.clientX, event.clientY));

        if (force || !this.fileOnWindow(event)) {
            this.windowActive = false;
            this.dropZoneActive = false;
            this.clearIdleTimer();
            this.createCallback(this.options.windowLeave);
        } else if (!onDropZone && this.dropZoneActive) {
            this.handleLeave(event.dataTransfer.items);
        }
    }

    /**
     * Handle drag entering the DropZone
     * @param {DataTransferItemList} files
     */
    handleEnter (files) {
        const { valid, text } = this.validator.validate(files, this.getFiles().length, this.getSize());

        this.clearIdleTimer();
        this.createCallback(this.options.dropZoneEnter, { valid, text });
        this.dropZoneActive = true;
    }

    /**
     * Handle drag leaving the DropZone
     * @param {DataTransferItemList} files
     */
    handleLeave (files) {
        const { valid, text } = this.validator.validate(files, this.getFiles().length, this.getSize());

        this.clearIdleTimer();
        this.createCallback(this.options.dropZoneLeave, { valid, text });
        this.dropZoneActive = false;
    }

    /**
     * A place we can do something with a file before we add it to the store.
     * @param {Object} file
     * @param {Object} meta
     * @return {Object} file object
     */
    processFile (file, meta) {
        this.size += file.size;
        return _.extend({}, {
            raw: file,
            thumbnail: DropZone.getFileThumbnail(file),
            id: _.uniqueId('DropZone_file_'),
            name: DropZone.getFileName(file),
            type: DropZone.getFileType(file),
            size: DropZone.getFileSize(file)
        }, meta);
    }

    /**
     * Handle a file rejection
     * @param {String} error
     */
    rejectFiles (error) {
        this.createCallback(this.options.filesRejected, { error, node: this.node });
    }

    /**
     * Return all files in the store or a file at a specified index
     * @param {Number} index
     * @return {Array}
     */
    getFiles (index = null) {
        return index === null ? this.files : this.files[index];
    }

    /**
     * Return total size of files on the DropZone
     * @return {Number}
     */
    getSize () {
        return this.size;
    }

    /**
     * Remove a file from the store using it's ID
     * @param  {String} id
     */
    removeFile (id) {
        this.files = this.files.filter(file => {
            if (file.id === id) {
                this.size -= file.raw.size;
            } else {
                return file;
            }
        });

        this.createCallback(this.options.fileRemoved);
    }

    /**
     * Check for a callback, if it exists, call it
     * @param  {Function} callback
     * @param  {Object}   data
     */
    createCallback (callback, data = {}) {
        data = _.extend({}, data, { instance: this });

        if (typeof callback === 'function') {
            callback(data);
        }
    }

    /**
     * Attach multiple events
     * @param {Array} nodes
     * @param {String} events
     * @param {Function} handler
     */
    attachMultipleListeners (nodes, events, handler) {
        nodes.forEach(node => {
            events.split(' ').forEach(event => {
                this.addEventAndStore(node, event, handler);
            });
        });
    }

    /**
     * Add event to event pool, the event pool is used to keep track of
     * bound events to allow us to properly remove them if needed
     * @param {HTMLElement} node
     * @param {Event} event
     * @param {Function} handler
     */
    addEventToPool (node, event, handler) {
        this.eventPool.push({ node, event, handler });
    }

    /**
     * Attach an event listener to a node and store in eventPool
     * @param {HTMLElement} node
     * @param {String} event
     * @param {Function} handler
     */
    addEventAndStore (node, event, handler) {
        this.addEventToPool(node, event, handler);
        node.addEventListener(event, handler);
    }

    /**
     * Remove any events that have been attached by DropZone
     */
    removeAllEvents () {
        this.eventPool = this.eventPool.reduce((pool, item) => {
            const { node, event, handler } = item;

            node.removeEventListener(event, handler);
            return pool;
        }, []);
    }

    /**
     * Prevent default event behaviour & propagation
     * @param  {Event} event
     */
    preventer (event) {
        event.preventDefault();
    }

    /**
     * Reset
     * - Remove any events attached by DropZone
     * - re-initialise default values, and event handlers
     */
    reset () {
        this.removeAllEvents();
        this.setup();
    }

    /**
     * Reset event tracker counters
     */
    resetEventTracker () {
        Object.keys(this.eventTracker).forEach(node => {
            Object.keys(this.eventTracker[node]).forEach(event => {
                this.eventTracker[node][event] = 0;
            });
        });
    }

    /**
     * Return a vanilla node from a jQuery
     * @param {Object} window
     * @param {Element} node
     * @returns {Element}
     */
    static getVanillaNode (window, node) {
        return window.$ && node instanceof window.$ ? node[0] : node;
    }

    /**
     * Format filename for printing
     * @param  {Object} file
     * @return {String}
     */
    static getFileName (file) {
        // strip out any directory path in our filename
        return file.name.replace(/.*[\\\/]/, '');
    }

    /**
     * Format type for printing
     * @param  {Object} file
     * @return {String}
     */
    static getFileType (file) {
        if (file.type.length) {
            return file.type;
        } else {
            return 'application/file';
        }
    }

    /**
     * Format size for printing
     * @param  {Object} file
     * @return {String}
     */
    static getFileSize (file) {
        return DropZone.formatBytes(file.size);
    }

    /**
     * Create url for image preview
     * @param  {Object} file
     * @return {String|Boolean}
     */
    static getFileThumbnail (file) {
        if (file.type.match(/\/(gif|jpeg|png|svg+xml|svg)/) && URL.createObjectURL) {
            return URL.createObjectURL(file);
        } else {
            return false;
        }
    }

    /**
     * Format bytes as unit relative to amount of bytes
     * @param  {Number} bytes
     * @param  {Number} decimal
     * @return {String}
     */
    static formatBytes (bytes, decimal = 1) {
        if (!bytes) {
            return '0 Byte';
        }

        const kb = 1000,
            sizes = ['Bytes', 'KB', 'MB', 'GB'],
            i = Math.floor(Math.log(bytes) / Math.log(kb));
        return `${parseFloat((bytes / Math.pow(kb, i)).toFixed(decimal))} ${sizes[i]}`;
    }
}
<commit_msg>make idle timeout duration configurable<commit_after>import _ from 'lodash';

export const defaults = {
    // HTML node that will become DropZone
    node: null,
    // limit of files within store
    maxFiles: 5,
    // limit total size of files (3e+8 === 300mb)
    maxSize: 3e+8,
    // a whitelist of file types to validate against
    whitelist: [],
    // duration of idle timer, if we get a mouseout event on the document
    // wait x ms before firing a window dragleave
    idle: 1000,
    // file enters window
    windowEnter: function () {},
    // file leaves window
    windowLeave: function () {},
    // drop on the window outside of the DropZone
    windowDrop: function () {},
    // drop on the DropZone
    dropZoneDrop: function () {},
    dropZoneEnter: function () {},
    // dragged files left DropZone
    dropZoneLeave: function () {},
    // removed file
    fileRemoved: function () {}
};

export default class DropZone {
    constructor (options, validator) {
        this.validator = validator;
        this.options = _.extend({}, defaults, options);
        // ensure we've got integers here, there is a chance these will come
        // in as strings from a DOM node's attributes
        this.options.maxFiles = parseInt(this.options.maxFiles);
        this.options.maxSize = parseInt(this.options.maxSize);
        // cache methods with context
        this.handleDropWithContext = this.handleDrop.bind(this);
        this.handleWindowEnterWithContext = this.handleWindowEnter.bind(this);
        this.handleWindowLeaveWithContext = this.handleWindowLeave.bind(this);
        this.startIdleTimerWithContext = this.startIdleTimer.bind(this);
        // a place to be used externally as a instance based cache;
        this.data = {};
        this.setup();
    }

    setup () {
        this.node = DropZone.getVanillaNode(window, this.options.node);

        if (!this.node) {
            throw new Error('DropZone requires a DOM node');
        }

        this.eventPool = [];
        this.files = [];
        this.idleTimer = null;
        this.eventTracker = { node: {}, window: {} };
        this.size = 0;
        this.windowActive = false;
        this.dropZoneActive = false;

        this.registerEvents();
        this.attachMultipleListeners(
            [window],
            'drag dragstart dragend dragover dragenter dragleave drop',
            this.preventer
        );
    }

    /**
     * Register drag & drop events
     */
    registerEvents () {
        // prevent defaults
        this.addEventAndStore(window, 'drag', this.preventer);
        this.addEventAndStore(window, 'dragstart', this.preventer);
        this.addEventAndStore(window, 'dragend', this.preventer);
        this.addEventAndStore(window, 'dragover', this.preventer);
        this.addEventAndStore(window, 'dragenter', this.preventer);
        this.addEventAndStore(window, 'dragleave', this.preventer);
        this.addEventAndStore(window, 'drop', this.preventer);
        // interactions
        this.addEventAndStore(window, 'dragenter', this.handleWindowEnterWithContext);
        this.addEventAndStore(window, 'dragleave', this.handleWindowLeaveWithContext);
        this.addEventAndStore(window, 'drop', this.handleDropWithContext);
        // attempt to handle missed callbacks
        // mouseout has proven to be _more_ reliable than dragleave
        this.addEventAndStore(document, 'mouseout', this.startIdleTimerWithContext);
    }

    /**
     * Trigger a timer to execute a manual windowLeave after x ms
     * this is to catch window dragLeave events that are missed at high speed
     * @param {Event} event
     */
    startIdleTimer (event) {
        this.clearIdleTimer();
        this.idleTimer = setTimeout(() => {

            if (this.windowActive || this.dropZoneActive) {
                this.handleWindowLeave(event, true);
            }

            this.idleTimer = null;
        }, this.options.idle);
    }

    /**
     * Clear the current timer
     */
    clearIdleTimer () {
        if (this.idleTimer !== null) {
            clearTimeout(this.idleTimer);
        }
    }

    /**
     * Handle drop event. Checks to see if we can add more files to the files store
     * @param  {Event} event
     */
    handleDrop (event) {
        const files = event.dataTransfer.items;

        // determine where the drop has taken place
        // dropped on the DropZone
        if (this.windowActive && this.dropZoneActive) {
            this.addFiles(files);
        // dropped on the window
        } else if (this.windowActive && !this.dropZoneActive) {
            this.createCallback(this.options.windowDrop, { files: this.files, node: this.node });
        }

        // reset DropZone state§
        this.windowActive = false;
        this.dropZoneActive = false;
        this.resetEventTracker();
    }

    /**
     * Validate and add files to the DropZone
     * @param {FileList} files
     * @param {Object} meta
     */
    addFiles (files, meta = {}) {
        const { valid, text } = this.validator.validate(files, this.getFiles().length, this.getSize());
        const processedFiles = [...files].map(
            file => this.processFile(file.getAsFile ? file.getAsFile() : file, meta)
        );

        if (valid) {
           this.files = [...this.files, ...processedFiles];
        }

        // fire dropped callback
        this.createCallback(this.options.dropZoneDrop, { files: processedFiles, node: this.node, valid, text });
    }

    /**
     * Determine if a file is currently on the window
     */
    fileOnWindow (event) {
        const { clientX, clientY } = event;
        const offWindow = clientX === 0 && clientY === 0;
        return !offWindow && document.elementFromPoint(clientX, clientY);
    }

    /**
     * Handle a file dragged into the window
     */
    handleWindowEnter (event) {
        const onWindow = this.fileOnWindow(event);
        const onDropZone = this.node.contains(onWindow);

        this.clearIdleTimer();

        if (onDropZone && !this.dropZoneActive) {
            this.handleEnter(event.dataTransfer.items);
        } else if (this.fileOnWindow(event) && !this.windowActive) {
            const files = event.dataTransfer.items;
            const { valid, text } = this.validator.validate(files, this.getFiles().length, this.getSize());

            this.createCallback(this.options.windowEnter, { valid, text });
            this.windowActive = true;
        }
    }

    /**
     * Handle a file being dragged off of the window
     * @param {Event} event
     * @param {Boolean} force
     */
    handleWindowLeave (event, force = false) {
        const onDropZone = this.node.contains(document.elementFromPoint(event.clientX, event.clientY));

        if (force || !this.fileOnWindow(event)) {
            this.windowActive = false;
            this.dropZoneActive = false;
            this.clearIdleTimer();
            this.createCallback(this.options.windowLeave);
        } else if (!onDropZone && this.dropZoneActive) {
            this.handleLeave(event.dataTransfer.items);
        }
    }

    /**
     * Handle drag entering the DropZone
     * @param {DataTransferItemList} files
     */
    handleEnter (files) {
        const { valid, text } = this.validator.validate(files, this.getFiles().length, this.getSize());

        this.clearIdleTimer();
        this.createCallback(this.options.dropZoneEnter, { valid, text });
        this.dropZoneActive = true;
    }

    /**
     * Handle drag leaving the DropZone
     * @param {DataTransferItemList} files
     */
    handleLeave (files) {
        const { valid, text } = this.validator.validate(files, this.getFiles().length, this.getSize());

        this.clearIdleTimer();
        this.createCallback(this.options.dropZoneLeave, { valid, text });
        this.dropZoneActive = false;
    }

    /**
     * A place we can do something with a file before we add it to the store.
     * @param {Object} file
     * @param {Object} meta
     * @return {Object} file object
     */
    processFile (file, meta) {
        this.size += file.size;
        return _.extend({}, {
            raw: file,
            thumbnail: DropZone.getFileThumbnail(file),
            id: _.uniqueId('DropZone_file_'),
            name: DropZone.getFileName(file),
            type: DropZone.getFileType(file),
            size: DropZone.getFileSize(file)
        }, meta);
    }

    /**
     * Handle a file rejection
     * @param {String} error
     */
    rejectFiles (error) {
        this.createCallback(this.options.filesRejected, { error, node: this.node });
    }

    /**
     * Return all files in the store or a file at a specified index
     * @param {Number} index
     * @return {Array}
     */
    getFiles (index = null) {
        return index === null ? this.files : this.files[index];
    }

    /**
     * Return total size of files on the DropZone
     * @return {Number}
     */
    getSize () {
        return this.size;
    }

    /**
     * Remove a file from the store using it's ID
     * @param  {String} id
     */
    removeFile (id) {
        this.files = this.files.filter(file => {
            if (file.id === id) {
                this.size -= file.raw.size;
            } else {
                return file;
            }
        });

        this.createCallback(this.options.fileRemoved);
    }

    /**
     * Check for a callback, if it exists, call it
     * @param  {Function} callback
     * @param  {Object}   data
     */
    createCallback (callback, data = {}) {
        data = _.extend({}, data, { instance: this });

        if (typeof callback === 'function') {
            callback(data);
        }
    }

    /**
     * Attach multiple events
     * @param {Array} nodes
     * @param {String} events
     * @param {Function} handler
     */
    attachMultipleListeners (nodes, events, handler) {
        nodes.forEach(node => {
            events.split(' ').forEach(event => {
                this.addEventAndStore(node, event, handler);
            });
        });
    }

    /**
     * Add event to event pool, the event pool is used to keep track of
     * bound events to allow us to properly remove them if needed
     * @param {HTMLElement} node
     * @param {Event} event
     * @param {Function} handler
     */
    addEventToPool (node, event, handler) {
        this.eventPool.push({ node, event, handler });
    }

    /**
     * Attach an event listener to a node and store in eventPool
     * @param {HTMLElement} node
     * @param {String} event
     * @param {Function} handler
     */
    addEventAndStore (node, event, handler) {
        this.addEventToPool(node, event, handler);
        node.addEventListener(event, handler);
    }

    /**
     * Remove any events that have been attached by DropZone
     */
    removeAllEvents () {
        this.eventPool = this.eventPool.reduce((pool, item) => {
            const { node, event, handler } = item;

            node.removeEventListener(event, handler);
            return pool;
        }, []);
    }

    /**
     * Prevent default event behaviour & propagation
     * @param  {Event} event
     */
    preventer (event) {
        event.preventDefault();
    }

    /**
     * Reset
     * - Remove any events attached by DropZone
     * - re-initialise default values, and event handlers
     */
    reset () {
        this.removeAllEvents();
        this.setup();
    }

    /**
     * Reset event tracker counters
     */
    resetEventTracker () {
        Object.keys(this.eventTracker).forEach(node => {
            Object.keys(this.eventTracker[node]).forEach(event => {
                this.eventTracker[node][event] = 0;
            });
        });
    }

    /**
     * Return a vanilla node from a jQuery
     * @param {Object} window
     * @param {Element} node
     * @returns {Element}
     */
    static getVanillaNode (window, node) {
        return window.$ && node instanceof window.$ ? node[0] : node;
    }

    /**
     * Format filename for printing
     * @param  {Object} file
     * @return {String}
     */
    static getFileName (file) {
        // strip out any directory path in our filename
        return file.name.replace(/.*[\\\/]/, '');
    }

    /**
     * Format type for printing
     * @param  {Object} file
     * @return {String}
     */
    static getFileType (file) {
        if (file.type.length) {
            return file.type;
        } else {
            return 'application/file';
        }
    }

    /**
     * Format size for printing
     * @param  {Object} file
     * @return {String}
     */
    static getFileSize (file) {
        return DropZone.formatBytes(file.size);
    }

    /**
     * Create url for image preview
     * @param  {Object} file
     * @return {String|Boolean}
     */
    static getFileThumbnail (file) {
        if (file.type.match(/\/(gif|jpeg|png|svg+xml|svg)/) && URL.createObjectURL) {
            return URL.createObjectURL(file);
        } else {
            return false;
        }
    }

    /**
     * Format bytes as unit relative to amount of bytes
     * @param  {Number} bytes
     * @param  {Number} decimal
     * @return {String}
     */
    static formatBytes (bytes, decimal = 1) {
        if (!bytes) {
            return '0 Byte';
        }

        const kb = 1000,
            sizes = ['Bytes', 'KB', 'MB', 'GB'],
            i = Math.floor(Math.log(bytes) / Math.log(kb));
        return `${parseFloat((bytes / Math.pow(kb, i)).toFixed(decimal))} ${sizes[i]}`;
    }
}
<|endoftext|>


<commit_before>var addic7edApi = require('addic7ed-api');
var fs = require('fs');
var Common = require('../common.js');
var Mongoose = require('mongoose');
var Mongo = require('../db/mongo.js');
var ScheduleManager = require('../schedule/scheduleManager.js');
var TvMaze = require('../libs/tvMaze.js');
var Logger = require('../log/logger.js');

exports.addic7edGetSubtitle = function (session, languages, bot, chat, sessionsList) {
    if(session.choosenEpisode.indexOf('-')===-1){
        getSingleEpisodeSubs(session, languages, bot, chat, sessionsList, session.choosenEpisode, true)
    }else{
        var start = +session.choosenEpisode.substr(0, session.choosenEpisode.indexOf('-'));
        var end = +session.choosenEpisode.substr(session.choosenEpisode.indexOf('-') + 1, session.choosenEpisode.length);
        while (start <= end){
            getSingleEpisodeSubs(session, languages, bot, chat, sessionsList, start, start == end),
            start++;
        }
    }
}

function getSingleEpisodeSubs (session, languages = [], bot, chat, sessionsList, episode, sendAmbiguousMessage) {
    Logger.logEvent("get", languages, session);
    addic7edApi.search(session.choosenSeries.show.name, session.choosenSeason,
        episode, languages).then(function (subtitlesList) {
            var subInfo = subtitlesList[0];
            if (subInfo != undefined) {
                var filename = session.choosenSeries.show.name + '_S' + session.choosenSeason + '_E' + episode + '.srt';
                addic7edApi.download(subInfo, filename).then(function () {
                    fs.access(filename, function (err) {
                        if (!err) {
                            console.log('Subtitles file saved.');
                            Common.removeSession(sessionsList, session);
                            bot.sendMessage(chat, Common.buildLinkMessage(subInfo.link));
                            bot.sendDocument(chat, filename).then(function () {
                                fs.unlinkSync(filename, function(err){
                                    if(err) console.log("error, file not deleted");
                                });
                            });
                            if (sendAmbiguousMessage && session.choosenSeries.show.name.indexOf("(") > -1 && session.choosenSeries.show.name.indexOf(")") > -1) {
                                bot.sendMessage(chat, Common.ambigousSubtitleMessage);
                            }
                        }
                    });
                 }).catch(function (err) {
                    console.log("error downloading subs");
                });
            }
            else
                bot.sendMessage(chat, Common.subtitleNotFoundInAddic7edMessage);
         }).catch(function (err) {
            console.log("error searching subs - ", session);
        });
}

exports.addic7edGetSubtitleAlert = function (alert, job, bot, doneJobInterval) {
    addic7edApi.search(alert.show_name, alert.nextepisode_season, alert.nextepisode_episode, alert.language).then(function (subtitlesList) {
        var subInfo = subtitlesList[0];
        if (subInfo != undefined) {
            var filename = alert.show_name + '_S' + alert.nextepisode_season + '_E' + alert.nextepisode_episode + "_" + alert.language + '.srt';
            addic7edApi.download(subInfo, filename).then(function () {
                fs.access(filename, function (err) {
                    if (!err) {
                        console.log('Subtitles file saved.');
                        Mongo.User.find({ alerts: alert._doc._id.toString() }, function (err, users) {
                            users.forEach(function (user) {
                                var userDoc = user._doc;
                                bot.sendMessage(userDoc.chatId, Common.newEpisodeAlertMessage(userDoc.first_name, alert._doc.show_name));
                                bot.sendMessage(userDoc.chatId, Common.buildLinkMessage(subInfo.link));
                                bot.sendDocument(userDoc.chatId, filename).then(function () {
                                    console.log("File sent to user " + userDoc.first_name);
                                });
                                if (Common.isAmbiguousTitle(alert._doc.show_name)) {
                                    bot.sendMessage(userDoc.chatId, Common.ambigousSubtitleMessage);
                                }
                            });
                            job.attrs.data.hasToBeRemoved = true;
                            fs.unlinkSync(filename, function(err){
                                if(err) console.log("error, file not deleted");
                            });
                            doneJobInterval();
                        });
                    }
                });
             }).catch(function (err) {
                console.log("error download subs - ", alert);
            });
        } else {
            job.attrs.data.hasToBeRemoved = false;
            doneJobInterval();
        }
     }).catch(function (err) {
        console.log("error searching subs - ", alert);
    });
}<commit_msg>fixed console.log<commit_after>var addic7edApi = require('addic7ed-api');
var fs = require('fs');
var Common = require('../common.js');
var Mongoose = require('mongoose');
var Mongo = require('../db/mongo.js');
var ScheduleManager = require('../schedule/scheduleManager.js');
var TvMaze = require('../libs/tvMaze.js');
var Logger = require('../log/logger.js');

exports.addic7edGetSubtitle = function (session, languages, bot, chat, sessionsList) {
    if(session.choosenEpisode.indexOf('-')===-1){
        getSingleEpisodeSubs(session, languages, bot, chat, sessionsList, session.choosenEpisode, true)
    }else{
        var start = +session.choosenEpisode.substr(0, session.choosenEpisode.indexOf('-'));
        var end = +session.choosenEpisode.substr(session.choosenEpisode.indexOf('-') + 1, session.choosenEpisode.length);
        while (start <= end){
            getSingleEpisodeSubs(session, languages, bot, chat, sessionsList, start, start == end),
            start++;
        }
    }
}

function getSingleEpisodeSubs (session, languages = [], bot, chat, sessionsList, episode, sendAmbiguousMessage) {
    Logger.logEvent("get", languages, session);
    addic7edApi.search(session.choosenSeries.show.name, session.choosenSeason,
        episode, languages).then(function (subtitlesList) {
            var subInfo = subtitlesList[0];
            if (subInfo != undefined) {
                var filename = session.choosenSeries.show.name + '_S' + session.choosenSeason + '_E' + episode + '.srt';
                addic7edApi.download(subInfo, filename).then(function () {
                    fs.access(filename, function (err) {
                        if (!err) {
                            //console.log('Subtitles file saved.');
                            Common.removeSession(sessionsList, session);
                            bot.sendMessage(chat, Common.buildLinkMessage(subInfo.link));
                            bot.sendDocument(chat, filename).then(function () {
                                fs.unlinkSync(filename, function(err){
                                    if(err) console.log("error, file not deleted");
                                });
                            });
                            if (sendAmbiguousMessage && session.choosenSeries.show.name.indexOf("(") > -1 && session.choosenSeries.show.name.indexOf(")") > -1) {
                                bot.sendMessage(chat, Common.ambigousSubtitleMessage);
                            }
                        }
                    });
                 }).catch(function (err) {
                    console.log("error downloading subs", err);
                });
            }
            else
                bot.sendMessage(chat, Common.subtitleNotFoundInAddic7edMessage);
         }).catch(function (err) {
            console.log("error searching subs - ", err);
        });
}

exports.addic7edGetSubtitleAlert = function (alert, job, bot, doneJobInterval) {
    addic7edApi.search(alert.show_name, alert.nextepisode_season, alert.nextepisode_episode, alert.language).then(function (subtitlesList) {
        var subInfo = subtitlesList[0];
        if (subInfo != undefined) {
            var filename = alert.show_name + '_S' + alert.nextepisode_season + '_E' + alert.nextepisode_episode + "_" + alert.language + '.srt';
            addic7edApi.download(subInfo, filename).then(function () {
                fs.access(filename, function (err) {
                    if (!err) {
                        //console.log('Subtitles file saved.');
                        Mongo.User.find({ alerts: alert._doc._id.toString() }, function (err, users) {
                            users.forEach(function (user) {
                                var userDoc = user._doc;
                                bot.sendMessage(userDoc.chatId, Common.newEpisodeAlertMessage(userDoc.first_name, alert._doc.show_name));
                                bot.sendMessage(userDoc.chatId, Common.buildLinkMessage(subInfo.link));
                                bot.sendDocument(userDoc.chatId, filename).then(function () {
                                    console.log("File sent to user " + userDoc.first_name);
                                });
                                if (Common.isAmbiguousTitle(alert._doc.show_name)) {
                                    bot.sendMessage(userDoc.chatId, Common.ambigousSubtitleMessage);
                                }
                            });
                            job.attrs.data.hasToBeRemoved = true;
                            fs.unlinkSync(filename, function(err){
                                if(err) console.log("error, file not deleted");
                            });
                            doneJobInterval();
                        });
                    }
                });
             }).catch(function (err) {
                console.log("error download subs - ", err);
            });
        } else {
            job.attrs.data.hasToBeRemoved = false;
            doneJobInterval();
        }
     }).catch(function (err) {
        console.log("error searching subs - ", err);
    });
}<|endoftext|>


<commit_before>/**
 * MLJLib
 * MeshLabJS Library
 *
 * Copyright(C) 2015
 * Paolo Cignoni
 * Visual Computing Lab
 * ISTI - CNR
 *
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See theGNU General Public License
 * (http://www.gnu.org/licenses/gpl.txt) for more details.
 *
 */

/**
 * @file Defines the functions to manage files
 * @author Stefano Gabriele
 */

/**
 * MLJ.core.File namespace
 * @namespace MLJ.core.File
 * @memberOf MLJ.core
 * @author Stefano Gabriele
 */
MLJ.core.File = {
    ErrorCodes: {
        EXTENSION: 1
    }
};

(function () {
    var _openedList = new MLJ.util.AssociativeArray();
    var nameList = new MLJ.util.AssociativeArray();

    function isExtensionValid(extension) {

        switch (extension) {
            case ".off":
            case ".obj":
            case ".ply":
            case ".stl":
            case ".vmi":
                return true;
        }

        return false;
    }

    function loadFile(file, onLoaded) {
        console.time("Read mesh file");

        if (!(file instanceof File)) {
            console.error("MLJ.file.open(file): the parameter 'file' must be a File instace.");
            onLoaded(false);
        }

        //Add file to opened list
        _openedList.set(file.name, file);

        //Extract file extension
        var pointPos = file.name.lastIndexOf('.');
        var extension = file.name.substr(pointPos);

        //Validate file extension
        if (!isExtensionValid(extension)) {
            var err = new MLJ.Error(
                    MLJ.core.meshfile.ErrorCodes.EXTENSION,
                    "MeshLabJs allows file format '.off', '.ply', '.vmi', '.obj' and '.stl'. \nTry again."
                    );

            MLJ.setError(err);

            onLoaded(false);
        }

        //Read file
        var fileReader = new FileReader();
        fileReader.readAsArrayBuffer(file);
        fileReader.onloadend = function (fileLoadedEvent) {
            console.log("Read file " + file.name + " size " + fileLoadedEvent.target.result.byteLength + " bytes");
            console.timeEnd("Read mesh file");
            //  Emscripten need a Arrayview so from the returned arraybuffer we must create a view of it as 8bit chars
            var int8buf = new Int8Array(fileLoadedEvent.target.result);
            FS.createDataFile("/", file.name, int8buf, true, true);

            console.time("Parsing Mesh Time");
            var CppMesh = new Module.CppMesh();
            var resOpen = CppMesh.openMesh(file.name);
            if (resOpen !== 0) {
                console.log("Ops! Error in Opening File. Try again.");
                FS.unlink(file.name);

                onLoaded(false);
            }

            console.timeEnd("Parsing Mesh Time");
            var ptrMesh = Opener.getMesh();

            var mf = new MLJ.core.MeshFile(file.name, ptrMesh);

            FS.unlink(file.name);

            onLoaded(true, mf);

        }; //end onloadend
    }

    /**
     * Reloads a mesh file by name
     * @param {String} name The name of the mesh file needs to be reloaded
     * @memberOf MLJ.core.File
     * @fires MLJ.core.File#MeshFileReloaded
     * @author Stefano Gabriele
     */
    this.reloadMeshFileByName = function (name) {
        var file = _openedList.getByKey(name);

        if (file === undefined) {
            console.warn("MLJ.file.reloadMeshFile(name): the scene not contains file '" + name + "'.");
            return false;
        }

        loadFile(file, function (loaded, meshFile) {
            if (loaded) {

                /**
                 *  Triggered when a mesh file is reloaded
                 *  @event MLJ.core.File#MeshFileReloaded
                 *  @type {Object}
                 *  @property {MLJ.core.MeshFile} meshFile The reloaded mesh file
                 *  @example
                 *
Event Interception:

                 *  $(document).on("MeshFileReloaded",
                 *      function (event, meshFile) {
                 *          //do something
                 *      }
                 *  );
                 */
                $(document).trigger("MeshFileReloaded", [meshFile]);
            } else {
                //else error in file reading
                console.log(MLJ.getLastError().message);
            }
        });
    };

    /**
     * Creates a new mesh file using the c++ functions bound to JavaScript
     * @param {String} name The name of the new mesh file
     * @memberOf MLJ.core.File
     * @returns {MLJ.core.MeshFile} The new mesh file
     * @author Stefano Gabriele
     */
    this.createCppMeshFile = function (name) {

        var nameAmount = nameList.getByKey(name);
        if (nameAmount === undefined) {
            nameList.set(name, 0);
        } else {
            nameAmount++;
            nameList.set(name, nameAmount);
            name += " " + nameAmount;
        }

        var CppMesh = new Module.CppMesh();
        var ptrMesh = CppMesh.getMesh();
        var mf = new MLJ.core.MeshFile(name, ptrMesh);

        //Indicates that the mesh is created by c++
        mf.cpp = true;
        return mf;
    };

    /**
     * Opens a mesh file or a list of mesh files
     * @param {(File | FileList)} file A single mesh file or a list of mesh files
     * @memberOf MLJ.core.File
     * @fires MLJ.core.File#MeshFileOpened
     * @author Stefano Gabriele
     */
    this.openMeshFile = function (file) {
        $(file).each(function (key, value) {
            loadFile(value, function (loaded, meshFile) {
                if (loaded) {
                    /**
                     *  Triggered when a mash file is opened
                     *  @event MLJ.core.File#MeshFileOpened
                     *  @type {Object}
                     *  @property {MLJ.core.MeshFile} meshFile The opened mesh file
                     *  @example
                     *
Event Interception:

                     *  $(document).on("MeshFileOpened",
                     *      function (event, meshFile) {
                     *          //do something
                     *      }
                     *  );
                     */
                    $(document).trigger("MeshFileOpened", [meshFile]);
                } else {//else error in file reading
                    console.log(MLJ.getLastError().message);
                }
            });
        });
    };

}).call(MLJ.core.File);
<commit_msg>Forgot an instance of opener...<commit_after>/**
 * MLJLib
 * MeshLabJS Library
 *
 * Copyright(C) 2015
 * Paolo Cignoni
 * Visual Computing Lab
 * ISTI - CNR
 *
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See theGNU General Public License
 * (http://www.gnu.org/licenses/gpl.txt) for more details.
 *
 */

/**
 * @file Defines the functions to manage files
 * @author Stefano Gabriele
 */

/**
 * MLJ.core.File namespace
 * @namespace MLJ.core.File
 * @memberOf MLJ.core
 * @author Stefano Gabriele
 */
MLJ.core.File = {
    ErrorCodes: {
        EXTENSION: 1
    }
};

(function () {
    var _openedList = new MLJ.util.AssociativeArray();
    var nameList = new MLJ.util.AssociativeArray();

    function isExtensionValid(extension) {

        switch (extension) {
            case ".off":
            case ".obj":
            case ".ply":
            case ".stl":
            case ".vmi":
                return true;
        }

        return false;
    }

    function loadFile(file, onLoaded) {
        console.time("Read mesh file");

        if (!(file instanceof File)) {
            console.error("MLJ.file.open(file): the parameter 'file' must be a File instace.");
            onLoaded(false);
        }

        //Add file to opened list
        _openedList.set(file.name, file);

        //Extract file extension
        var pointPos = file.name.lastIndexOf('.');
        var extension = file.name.substr(pointPos);

        //Validate file extension
        if (!isExtensionValid(extension)) {
            var err = new MLJ.Error(
                    MLJ.core.meshfile.ErrorCodes.EXTENSION,
                    "MeshLabJs allows file format '.off', '.ply', '.vmi', '.obj' and '.stl'. \nTry again."
                    );

            MLJ.setError(err);

            onLoaded(false);
        }

        //Read file
        var fileReader = new FileReader();
        fileReader.readAsArrayBuffer(file);
        fileReader.onloadend = function (fileLoadedEvent) {
            console.log("Read file " + file.name + " size " + fileLoadedEvent.target.result.byteLength + " bytes");
            console.timeEnd("Read mesh file");
            //  Emscripten need a Arrayview so from the returned arraybuffer we must create a view of it as 8bit chars
            var int8buf = new Int8Array(fileLoadedEvent.target.result);
            FS.createDataFile("/", file.name, int8buf, true, true);

            console.time("Parsing Mesh Time");
            var CppMesh = new Module.CppMesh();
            var resOpen = CppMesh.openMesh(file.name);
            if (resOpen !== 0) {
                console.log("Ops! Error in Opening File. Try again.");
                FS.unlink(file.name);

                onLoaded(false);
            }

            console.timeEnd("Parsing Mesh Time");
            var ptrMesh = CppMesh.getMesh();

            var mf = new MLJ.core.MeshFile(file.name, ptrMesh);

            FS.unlink(file.name);

            onLoaded(true, mf);

        }; //end onloadend
    }

    /**
     * Reloads a mesh file by name
     * @param {String} name The name of the mesh file needs to be reloaded
     * @memberOf MLJ.core.File
     * @fires MLJ.core.File#MeshFileReloaded
     * @author Stefano Gabriele
     */
    this.reloadMeshFileByName = function (name) {
        var file = _openedList.getByKey(name);

        if (file === undefined) {
            console.warn("MLJ.file.reloadMeshFile(name): the scene not contains file '" + name + "'.");
            return false;
        }

        loadFile(file, function (loaded, meshFile) {
            if (loaded) {

                /**
                 *  Triggered when a mesh file is reloaded
                 *  @event MLJ.core.File#MeshFileReloaded
                 *  @type {Object}
                 *  @property {MLJ.core.MeshFile} meshFile The reloaded mesh file
                 *  @example
                 *
Event Interception:

                 *  $(document).on("MeshFileReloaded",
                 *      function (event, meshFile) {
                 *          //do something
                 *      }
                 *  );
                 */
                $(document).trigger("MeshFileReloaded", [meshFile]);
            } else {
                //else error in file reading
                console.log(MLJ.getLastError().message);
            }
        });
    };

    /**
     * Creates a new mesh file using the c++ functions bound to JavaScript
     * @param {String} name The name of the new mesh file
     * @memberOf MLJ.core.File
     * @returns {MLJ.core.MeshFile} The new mesh file
     * @author Stefano Gabriele
     */
    this.createCppMeshFile = function (name) {

        var nameAmount = nameList.getByKey(name);
        if (nameAmount === undefined) {
            nameList.set(name, 0);
        } else {
            nameAmount++;
            nameList.set(name, nameAmount);
            name += " " + nameAmount;
        }

        var CppMesh = new Module.CppMesh();
        var ptrMesh = CppMesh.getMesh();
        var mf = new MLJ.core.MeshFile(name, ptrMesh);

        //Indicates that the mesh is created by c++
        mf.cpp = true;
        return mf;
    };

    /**
     * Opens a mesh file or a list of mesh files
     * @param {(File | FileList)} file A single mesh file or a list of mesh files
     * @memberOf MLJ.core.File
     * @fires MLJ.core.File#MeshFileOpened
     * @author Stefano Gabriele
     */
    this.openMeshFile = function (file) {
        $(file).each(function (key, value) {
            loadFile(value, function (loaded, meshFile) {
                if (loaded) {
                    /**
                     *  Triggered when a mash file is opened
                     *  @event MLJ.core.File#MeshFileOpened
                     *  @type {Object}
                     *  @property {MLJ.core.MeshFile} meshFile The opened mesh file
                     *  @example
                     *
Event Interception:

                     *  $(document).on("MeshFileOpened",
                     *      function (event, meshFile) {
                     *          //do something
                     *      }
                     *  );
                     */
                    $(document).trigger("MeshFileOpened", [meshFile]);
                } else {//else error in file reading
                    console.log(MLJ.getLastError().message);
                }
            });
        });
    };

}).call(MLJ.core.File);
<|endoftext|>


<commit_before>/* global Infinity */

/**
 * ownCloud - fuel
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Christoph Wurst <christoph@winzerhof-wurst.at>
 * @copyright Christoph Wurst 2015
 */

define(function (require) {
	'use strinct';

	var Backbone = require('backbone');

	return Backbone.Model.extend({
		defaults: {
			id: null,
			vehicleId: null,
			date: null,
			odo: 0.0,
			fuel: 0.0,
			distance: 0.0,
			consumption: Infinity,
			predecessor: null,
			active: false
		},
		initialize: function() {
			this.on('change:predecessor', function(record) {
				record.set('distance', this.getDistance());
			});
			this.on('change:distance', function(record) {
				record.set('consumption', this.getConsumption());
			});
		},
		getDistance: function () {
			var predecessor = this.get('predecessor');
			if (predecessor) {
				return this.get('odo') - predecessor.get('odo');
			}
			return Infinity;
		},
		getConsumption: function() {
			var consumption = this.get('fuel') / this.getDistance();
			return consumption.toFixed(2);
		}
	});
});<commit_msg>fix consumption calculation<commit_after>/* global Infinity */

/**
 * ownCloud - fuel
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Christoph Wurst <christoph@winzerhof-wurst.at>
 * @copyright Christoph Wurst 2015
 */

define(function (require) {
	'use strinct';

	var Backbone = require('backbone');

	return Backbone.Model.extend({
		defaults: {
			id: null,
			vehicleId: null,
			date: null,
			odo: 0.0,
			fuel: 0.0,
			distance: 0.0,
			consumption: Infinity,
			predecessor: null,
			active: false
		},
		initialize: function() {
			this.on('change:predecessor', function(record) {
				record.set('distance', this.getDistance());
			});
			this.on('change:distance', function(record) {
				record.set('consumption', this.getConsumption());
			});
		},
		getDistance: function () {
			var predecessor = this.get('predecessor');
			if (predecessor) {
				return this.get('odo') - predecessor.get('odo');
			}
			return Infinity;
		},
		getConsumption: function() {
			var consumption = this.get('fuel') / this.getDistance() * 100;
			return consumption.toFixed(2);
		}
	});
});<|endoftext|>


<commit_before>(function(BANTP) {
	BANTP.Workspace = Backbone.View.extend({
		el : "body",
		events : {
			"click .sidebar a" : "activateModule"
		},
		initialize : function() {
			log("Initializing Bad Ass New Tab Page!");
			this.model = new BANTP.WorkspaceData();

			// Instantiate only the necessary dashboard module
			var Dashboard = loader.module('dashboard');
			this.Dashboard = new Dashboard.Workspace();
		},
		render : function() {
			// Put the thing on the page!
			var self = this;

			this.Dashboard.render();

			return this;
		},
		activateModule : function(e) {
			console.time("Module Activation");
			console.groupCollapsed("Module Activation");
			e.preventDefault();

			var data = $(e.currentTarget).data();
			var toLoad = "";
			var name = data.module || data.app;

			if( data.hasOwnProperty("module") ) {
				toLoad = loader.module( data.module );
				log("Loading Module " + name );
			} else if( data.hasOwnProperty("app") ) {
				toLoad = loader.app( data.app );
				log("Loading App " + name );
			}

			this.model.set({ currentModule : name });

			var IncomingModule = new toLoad.Workspace();
			IncomingModule.render();

			log("Module " + name + " activated!");

			console.groupEnd();
			console.timeEnd("Module Activation");
		}
	});

	BANTP.WorkspaceData = Backbone.Model.extend({
		idAttribute : '_id',
		defaults : {
			currentModule : "dashboard"
		},
		initialize : function() {
			log("Initializing BANTP Workspace Data Model");
			// Initialization
			this.on('change', function() {
				log("BANTP Workspace data update", this.toJSON() );
			});
		}
	});

})(loader.module('bantp'));<commit_msg>Decoupled dashboard from BANTP application<commit_after>(function(BANTP) {
	BANTP.Workspace = Backbone.View.extend({
		el : "body",
		events : {
			"click .sidebar a" : "activateModule"
		},
		initialize : function() {
			log("Initializing Bad Ass New Tab Page!");
			this.model = new BANTP.WorkspaceData();
		},
		render : function() {
			// Put the thing on the page!
			var self = this;

			// Instantiate only the necessary dashboard module
			var DashboardModule = loader.module('dashboard');
			var Dashboard = new DashboardModule.Workspace();

			Dashboard.render();

			return this;
		},
		activateModule : function(e) {
			console.time("Module Activation");
			console.groupCollapsed("Module Activation");
			e.preventDefault();

			var data = $(e.currentTarget).data();
			var toLoad = "";
			var name = data.module || data.app;

			if( data.hasOwnProperty("module") ) {
				toLoad = loader.module( data.module );
				log("Loading Module " + name );
			} else if( data.hasOwnProperty("app") ) {
				toLoad = loader.app( data.app );
				log("Loading App " + name );
			}

			this.model.set({ currentModule : name });

			var IncomingModule = new toLoad.Workspace();
			IncomingModule.render();

			log("Module " + name + " activated!");

			console.groupEnd();
			console.timeEnd("Module Activation");
		}
	});

	BANTP.WorkspaceData = Backbone.Model.extend({
		idAttribute : '_id',
		defaults : {
			currentModule : "dashboard"
		},
		initialize : function() {
			log("Initializing BANTP Workspace Data Model");
			// Initialization
			this.on('change', function() {
				log("BANTP Workspace data update", this.toJSON() );
			});
		}
	});

})(loader.module('bantp'));<|endoftext|>