<commit_before>// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************

// -- This is a login command --
Cypress.Commands.add("loginAdmin", (username, password, assertLoggedIn = true) => {
    cy.login("admin", "changeme");
});

Cypress.Commands.add("loginStandard", (username, password, assertLoggedIn = true) => {
    cy.login("tony.wade@example.com", "abc123");
});

Cypress.Commands.add("login", (username, password, assertLoggedIn = true) => {
    cy.visit("/");
    cy.wait(150);
    cy.get("#UserBox").type(username);
    cy.get("#PasswordBox").type(password);
    cy.get("form").submit();
    if (assertLoggedIn) {
        cy.location('pathname').should('include', "/Menu.php");
    }
});

<commit_msg>dashboard v2<commit_after>// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************

// -- This is a login command --
Cypress.Commands.add("loginAdmin", (username, password, assertLoggedIn = true) => {
    cy.login("admin", "changeme");
});

Cypress.Commands.add("loginStandard", (username, password, assertLoggedIn = true) => {
    cy.login("tony.wade@example.com", "abc123");
});

Cypress.Commands.add("login", (username, password, assertLoggedIn = true) => {
    cy.visit("/");
    cy.wait(150);
    cy.get("#UserBox").type(username);
    cy.get("#PasswordBox").type(password);
    cy.get("form").submit();
    if (assertLoggedIn) {
        cy.location('pathname').should('include', "/v2/dashboard");
    }
});

<|endoftext|>


<commit_before>var parser = require("./parser").parser;

var INDENT = 'INDENT';
var DEDENT = 'DEDENT';
var PREVIOUS_TOKEN = null;
var STACK = [0];

// REFERENCE https://github.com/zaach/jison/issues/47

var oldAction = parser.lexer.performAction;
parser.lexer.performAction = function(lineno, yy){
	var ret = oldAction.apply(this, arguments);
	if(ret != null)
	{
		var currentToken =parser.terminals_[ret];
		var isNewline = PREVIOUS_TOKEN == 'NEWLINE' || PREVIOUS_TOKEN == null;

		console.log("matched '%s' with token '%s'", ret, currentToken);
		if(yy.match != yy.yytext)
		{
			console.log("Replaced: '%s' with: '%s'", yy.match, yy.yytext);
		}
		if(isNewline)
		{
			lexIndentation(yy, currentToken);
		}
		PREVIOUS_TOKEN = currentToken;
	}
	else
	{
		console.log("Ignored '%s'", yy.match);
	}
	return ret;
};

function lexIndentation(yy, currentToken)
{
	var isIndented = currentToken == 'WHITESPACE';
	var level = isIndented ? yy.yytext.length : 0;
	previousLevel = STACK[STACK.length - 1];
	while(level < previousLevel)
	{
		STACK.pop();
		previousLevel = STACK[STACK.length - 1];
		parser.lexer.yy.match = 'DEDENT';
	}
	if(level > previousLevel)
	{
		STACK.push(level);
		parser.lexer.yy.match = 'INDENT';
	}
}

var input = process.argv[2];
console.log(parser.parse(input));
<commit_msg>Changed print statement in runner to be more useful and actually print the underlying content<commit_after>var parser = require("./parser").parser;

var INDENT = 'INDENT';
var DEDENT = 'DEDENT';
var PREVIOUS_TOKEN = null;
var STACK = [0];

// REFERENCE https://github.com/zaach/jison/issues/47

var oldAction = parser.lexer.performAction;
parser.lexer.performAction = function(lineno, yy){
	var ret = oldAction.apply(this, arguments);
	if(ret != null)
	{
		var currentToken =parser.terminals_[ret];
		var isNewline = PREVIOUS_TOKEN == 'NEWLINE' || PREVIOUS_TOKEN == null;

		console.log("matched '%s' with token '%s'", yy.match, currentToken);
		if(yy.match != yy.yytext)
		{
			console.log("Replaced: '%s' with: '%s'", yy.match, yy.yytext);
		}
		if(isNewline)
		{
			lexIndentation(yy, currentToken);
		}
		PREVIOUS_TOKEN = currentToken;
	}
	else
	{
		console.log("Ignored '%s'", yy.match);
	}
	return ret;
};

function lexIndentation(yy, currentToken)
{
	var isIndented = currentToken == 'WHITESPACE';
	var level = isIndented ? yy.yytext.length : 0;
	previousLevel = STACK[STACK.length - 1];
	while(level < previousLevel)
	{
		STACK.pop();
		previousLevel = STACK[STACK.length - 1];
		parser.lexer.yy.match = 'DEDENT';
	}
	if(level > previousLevel)
	{
		STACK.push(level);
		parser.lexer.yy.match = 'INDENT';
	}
}

var input = process.argv[2];
console.log(parser.parse(input));
<|endoftext|>


<commit_before>feather.ns("blog");
(function() {

	blog.latestposts = feather.widget.create({
		name : "blog.latestposts",
		path : "widgets/latestposts/",
		prototype : {
			initialize : function($super, options) {
				$super(options);
				var me = this;
			},
      onReady: function() {
        var me = this;
        feather.auth.api.on('authenticated', function() {
          me.checkUser();
        }, me); //pass disposable object to track disposal to clean up handlers (disposable objects are expected to fire a 'disposed' event)
        this.bindUI();
      },
      bindUI: function() {
        var me = this;

        // Bind a click event to the headers to expand / collapse them.
        me.domEvents.bind(me.get(".blogentry h3"), "click", function(event) {
          var target = $(this); //note: 'this' inside jQuery .bind functions === the element that triggered the event
          if (target[0]) {
            var content = target.next('p');
            if (content.hasClass('collapsed')) {
              content.removeClass('collapsed');
              target.val('-');
            } else {
              content.addClass('collapsed');
              target.val('+');
            }
          }
        });
      },
      refreshPosts: function() {
        var me = this;
        me.server_getPosts(function(args) {
          if (args.success) {
            me.domEvents.unbindAll(); //avoid memory leaks
            me.get("#list").html("");
            $.tmpl(me.templates.posts, {result: args.result}).appendTo(me.get("#list"));
            me.bindUI();
            me.checkUser();
          }
        });
      },
      checkUser: function() {
        var me = this;
        if (feather.auth.user && (feather.auth.user.hasAnyAuthority(['admin', 'editor']))) {
          $('.blogentry h3').prepend(function(index, html) {
            return '<input type="button" value="Edit" postid="' + $($('.blogentry h3')[index]).attr('postid') + '" class="btnEditPost" />';
          });
          me.domEvents.bind(me.get(".btnEditPost"), "click", function(event) {
            event.preventDefault();
            var postId = $(this).attr('postId');
            var post = {
              id: postId,
              summary: $('#'+postId+'-summary').text(),
              post: $('#'+postId+'-post').text()
            };
            me.fire("editPost", {post: post});
          });
        }
      }
		}
	});

})();
<commit_msg>use stopPropagation instead of preventDefault;<commit_after>feather.ns("blog");
(function() {

	blog.latestposts = feather.widget.create({
		name : "blog.latestposts",
		path : "widgets/latestposts/",
		prototype : {
			initialize : function($super, options) {
				$super(options);
				var me = this;
			},
      onReady: function() {
        var me = this;
        feather.auth.api.on('authenticated', function() {
          me.checkUser();
        }, me); //pass disposable object to track disposal to clean up handlers (disposable objects are expected to fire a 'disposed' event)
        this.bindUI();
      },
      bindUI: function() {
        var me = this;

        // Bind a click event to the headers to expand / collapse them.
        me.domEvents.bind(me.get(".blogentry h3"), "click", function(event) {
          var target = $(this); //note: 'this' inside jQuery .bind functions === the element that triggered the event
          if (target[0]) {
            var content = target.next('p');
            if (content.hasClass('collapsed')) {
              content.removeClass('collapsed');
              target.val('-');
            } else {
              content.addClass('collapsed');
              target.val('+');
            }
          }
        });
      },
      refreshPosts: function() {
        var me = this;
        me.server_getPosts(function(args) {
          if (args.success) {
            me.domEvents.unbindAll(); //avoid memory leaks
            me.get("#list").html("");
            $.tmpl(me.templates.posts, {result: args.result}).appendTo(me.get("#list"));
            me.bindUI();
            me.checkUser();
          }
        });
      },
      checkUser: function() {
        var me = this;
        if (feather.auth.user && (feather.auth.user.hasAnyAuthority(['admin', 'editor']))) {
          $('.blogentry h3').prepend(function(index, html) {
            return '<input type="button" value="Edit" postid="' + $($('.blogentry h3')[index]).attr('postid') + '" class="btnEditPost" />';
          });
          me.domEvents.bind(me.get(".btnEditPost"), "click", function(event) {
            event.stopPropagation();
            var postId = $(this).attr('postId');
            var post = {
              id: postId,
              summary: $('#'+postId+'-summary').text(),
              post: $('#'+postId+'-post').text()
            };
            me.fire("editPost", {post: post});
          });
        }
      }
		}
	});

})();
<|endoftext|>


<commit_before>(function (window, Q, $, undefined) {

/**
 * @module Q
 */

/**
 * YUIDoc description goes here
 * @class Q video
 * @constructor
 * @param {Object} [options] Override various options for this tool
 *  @param {String} [options.url] URL of video source
 *  @param {string} [options.start] start position in milliseconds
 *  @param {string} [options.clipStart] Clip start position in milliseconds
 *  @param {string} [options.clipEnd] Clip end position in milliseconds
 *  @param {boolean} [options.autoplay=false] If true - start play on load
 */
Q.Tool.define("Q/video", function (options) {
	var tool = this;
	var state = tool.state;

	if (state.url) {
		state.url = state.url.interpolate({ "baseUrl": Q.info.baseUrl });
	}

	tool.adapters = {};

	tool.adapters.mp4 = {
		init: function () {
			var options = {
				sources: [{
					src: state.url,
					type: 'video/mp4'
				}]
			};

			tool.initVideojsPlayer(options);
		}
	};
	tool.adapters.webm = {
		init: function () {
			var options = {
				sources: [{
					src: state.url,
					type: 'video/webm'
				}]
			};

			tool.initVideojsPlayer(options);
		}
	};
	tool.adapters.ogg = {
		init: function () {
			var options = {
				sources: [{
					src: state.url,
					type: 'video/ogg'
				}]
			};

			tool.initVideojsPlayer(options);
		}
	};
	tool.adapters.youtube = {
		init: function () {
			Q.addScript("{{Q}}/js/videojs/plugins/YouTube.js", function () {
				var options = {
					techOrder: ["youtube"],
					sources: [{
						src: state.url,
						type: 'video/youtube'
					}],
					youtube: {
						ytControls: 2
					}
				};

				tool.initVideojsPlayer(options);
			});
		}
	};
	tool.adapters.vimeo = {
		init: function () {
			Q.addScript("{{Q}}/js/videojs/plugins/Vimeo.js", function () {
				var options = {
					techOrder: ["vimeo"],
					sources: [{
						src: state.url,
						type: 'video/vimeo'
					}],
					vimeo: {
						ytControls: 2
					}
				};

				tool.initVideojsPlayer(options);
			});
		}
	};
	tool.adapters.twitch = {
		init: function () {
			var element = tool.element;
			var throttle = state.throttle;

			var newA = document.createElement('A');
			newA.href = state.url;
			var host = newA.hostname;

			element.classList.add("Q_video_twitch");

			var options = {
				autoplay: false
				//channel: "<channel ID>",
				//video: "782042263",
				//collection: "<collection ID>"
			};

			// convert start time to pass as option
			if (state.start) {
				options.time = state.start.convertTimeToString(false).replace(/:/, 'h').replace(/:/, 'm') + 's';
			}
			if (state.clipStart) {
				options.time = state.clipStart.convertTimeToString(false).replace(/:/, 'h').replace(/:/, 'm') + 's';
			}

			var videoId = state.url.match(/\/videos\/([0-9]+).*$/);
			var channel = state.url.match(new RegExp(host + "/(\\w+)"));
			if (videoId) {
				options.video = videoId[1];
			} else if (channel) {
				options.channel = channel[1];
			} else {
				throw new Q.Error("Q/video/twitch: none of videoId or channel filled");
			}

			Q.addScript("{{Q}}/js/twitch/lib.js", function () {
				state.player = new Twitch.Player(element, options);
				state.player.currentTime = function (time) {
					state.player.seek(time);
				};

				var _getCurrentPostion = function () {
					return Math.trunc(state.player.getCurrentTime() * 1000);
				};

				var onPlay = Q.throttle(function () {
					state.currentPosition = _getCurrentPostion();
					console.log("Started at position " + state.currentPosition + " milliseconds");
					Q.handle(state.onPlay, tool);

					// clear timer if exist
					tool.clearPlayInterval();

					// start new timer to change needed layout params during play
					state.playIntervalID = setInterval(function(){
						state.currentPosition = _getCurrentPostion();
						Q.handle(state.onPlaying, tool);
					}, 100);
				}, throttle);
				var onPause = Q.throttle(function () {
					var position = _getCurrentPostion();
					console.log("Paused at position " + position + " milliseconds");
					Q.handle(state.onPause, tool);
				}, throttle);
				var onEnded = Q.throttle(function () {
					var position = _getCurrentPostion();
					console.log("Seeked at position " + position + " milliseconds");
					Q.handle(state.onEnded, tool);
				}, throttle);

				state.player.addEventListener(Twitch.Player.PLAY, onPlay);
				state.player.addEventListener(Twitch.Player.PAUSE, onPause);
				state.player.addEventListener(Twitch.Player.ENDED, onEnded);
			});
		}
	};

	//tool.adapters.youtube = tool.adapters.vimeo = tool.adapters.mp4 = tool.adapters.webm = tool.adapters.general;

	var p = Q.pipe(['stylesheet', 'text', 'scripts'], function (params, subjects) {
		tool.text = params.text[1].video;
		tool.implement();
	});

	Q.addStylesheet(["{{Q}}/css/videojs.css", "{{Q}}/css/video.css"], p.fill('stylesheet'), { slotName: 'Q' });
	Q.addScript("{{Q}}/js/videojs/lib.js", p.fill('scripts'));
	Q.Text.get('Q/content', p.fill('text'));
},

{
	url: null,
	autoplay: false,
	throttle: 10,
	currentPosition: 0,
	start: null,
	clipStart: null,
	clipEnd: null,
	videojsOptions: {
		controls: true
	},
	onSuccess: new Q.Event(),
	onError: new Q.Event(function (message) {
		alert('Flie upload error' + (message ? ': ' + message : '') + '.');
	}, 'Q/video'),
	onFinish: new Q.Event(),
	onLoad: new Q.Event(),
	onPlay: new Q.Event(function () {
		this.checkClip();
	}),
	onPlaying: new Q.Event(function () {
		this.checkClip();
	}),
	onPause: new Q.Event(function () {
		this.clearPlayInterval();
	}),
	onSeek: new Q.Event(),
	onEnded: new Q.Event(function () {
		// stop timer if exist
		this.clearPlayInterval();
	})
},

{
	/**
	 * Check clip borders and pause if outside
	 * @method checkClip
	 */
	checkClip: function () {
		var tool = this;
		var state = this.state;

		// clipStart handler
		if (state.clipStart && state.currentPosition < state.clipStart) {
			tool.setCurrentPosition(state.clipStart);
		}
		// clipStart handler
		if (state.clipEnd && state.currentPosition > state.clipEnd) {
			tool.pause();
			tool.setCurrentPosition(state.clipEnd);
		}
	},
	/**
	 * Refreshes the appearance of the tool completely
	 * @method implement
	 */
	implement: function () {
		var tool = this;

		var adapterName = tool.adapterNameFromUrl();
		tool.adapters[adapterName].init();
	},
	/**
	 *
	 * @method initVideojsPlayer
	 */
	initVideojsPlayer: function (options) {
		var tool = this;
		var state = this.state;
		var throttle = state.throttle;

		options = Q.extend(state.videojsOptions, options);

		Q.Template.render('Q/video/videojs', {
			autoplay: state.autoplay ? 'autoplay' : '',
		}, function (err, html) {
			tool.element.innerHTML = html;

			var _getCurrentPostion = function () {
				return Math.trunc(state.player.currentTime() * 1000);
			};

			var onPlay = Q.throttle(function () {
				var position = state.currentPosition || _getCurrentPostion();
				console.log("Started at position " + position + " milliseconds");
				Q.handle(state.onPlay, tool, [position]);
			}, throttle);
			var onPause = Q.throttle(function () {
				var position = state.currentPosition || _getCurrentPostion();
				console.log("Paused at position " + position + " milliseconds");
				Q.handle(state.onPause, tool, [position]);
			}, throttle);
			var onSeek = Q.throttle(function () {
				var position = state.currentPosition || _getCurrentPostion();
				console.log("Seeked at position " + position + " milliseconds");
				Q.handle(state.onSeek, tool, [position]);
			}, throttle);
			var onEnded = Q.throttle(function () {
				var position = state.currentPosition || _getCurrentPostion();
				console.log("Seeked at position " + position + " milliseconds");
				Q.handle(state.onEnded, tool, [position]);
			}, throttle);

			state.player = videojs($("video", tool.element)[0], options, function onPlayerReady() {
				videojs.log('Your player is ready!');

				if (state.clipStart || state.start) {
					var start = (state.clipStart || state.start)/1000;
					this.currentTime(start);
					state.currentPosition = start;
				} else {
					state.currentPosition = 0;
				}

				this.on('play', function () {
					onPlay();
				});

				this.on('pause', function () {
					onPause();
				});

				this.on('seeking', function() {
					onPause();
				});

				this.on('waiting', function() {
					onPause();
				});

				this.on('seeked', function() {
					onPlay();
					onSeek();
				});

				this.on('ended', function() {
					onPause();
					onEnded();
				});

				// update currentPosition array on play
				this.on('timeupdate', function() {
					setTimeout(function(){
						state.currentPosition = _getCurrentPostion();
						Q.handle(state.onPlaying, tool, [state.currentPosition]);
					}, 500);
				});
			});
		});
	},
	/**
	 * @method play
	 */
	play: function () {
		this.state.player && this.state.player.play();
	},
	/**
	 * @method pause
	 */
	pause: function () {
		this.state.player && this.state.player.pause();
	},
	/**
	 * @method setCurrentPosition
	 * @param {integer} position in milliseonds
	 */
	setCurrentPosition: function (position) {
		this.state.player && this.state.player.currentTime(position/1000);
	},
	/**
	 * @method getCurrentPosition
	 */
	getCurrentPosition: function () {
		return this.state.currentPosition;
	},
	/**
	 * Detect adapter from url
	 * @method adapterNameFromUrl
	 */
	adapterNameFromUrl: function () {
		var url = this.state.url;
		if (!url) {
			throw new Q.Exception(this.id + ": url is required");
		}
		var newA = document.createElement('A');
		newA.href = url;
		var host = newA.hostname;

		if (host.includes("youtube.com") || host.includes("youtu.be")) {
			return 'youtube';
		} else if (host.includes("vimeo.com")) {
			return 'vimeo';
		} else if (host.includes("twitch")) {
			return 'twitch';
		}

		var ext = url.split('.').pop();
		switch(ext) {
			case 'webm': return 'webm';
			case 'ogg': return 'ogg';
			default: return 'mp4';
		}
		//throw new Q.Exception(this.id + ': No adapter for this URL');
	},
	/**
	 *
	 * @method clearPlayInterval
	 */
	clearPlayInterval: function () {
		var playIntervalID = this.state.playIntervalID;
		if (playIntervalID) {
			clearInterval(playIntervalID);
			playIntervalID = false;
		}
	},
	Q: {
		beforeRemove: function () {
			this.clearPlayInterval();

			// if videojs, call dispose to kill this player with events, triggers, dom etc
			if (Q.getObject("player.dispose", this.state)) {
				this.state.player.dispose();
			}
		}
	}
});

Q.Template.set('Q/video/videojs',
	'<video preload="auto" controls class="video-js vjs-default-skin vjs-4-3" width="100%" height="auto" {{autoplay}}/>'
);

})(window, Q, jQuery);<commit_msg>Fixed Q/video tool.<commit_after>(function (window, Q, $, undefined) {

/**
 * @module Q
 */

/**
 * YUIDoc description goes here
 * @class Q video
 * @constructor
 * @param {Object} [options] Override various options for this tool
 *  @param {String} [options.url] URL of video source
 *  @param {string} [options.start] start position in milliseconds
 *  @param {string} [options.clipStart] Clip start position in milliseconds
 *  @param {string} [options.clipEnd] Clip end position in milliseconds
 *  @param {boolean} [options.autoplay=false] If true - start play on load
 */
Q.Tool.define("Q/video", function (options) {
	var tool = this;
	var state = tool.state;

	if (state.url) {
		state.url = state.url.interpolate({ "baseUrl": Q.info.baseUrl });
	}

	tool.adapters = {};

	tool.adapters.mp4 = {
		init: function () {
			var options = {
				sources: [{
					src: state.url,
					type: 'video/mp4'
				}]
			};

			tool.initVideojsPlayer(options);
		}
	};
	tool.adapters.webm = {
		init: function () {
			var options = {
				sources: [{
					src: state.url,
					type: 'video/webm'
				}]
			};

			tool.initVideojsPlayer(options);
		}
	};
	tool.adapters.ogg = {
		init: function () {
			var options = {
				sources: [{
					src: state.url,
					type: 'video/ogg'
				}]
			};

			tool.initVideojsPlayer(options);
		}
	};
	tool.adapters.youtube = {
		init: function () {
			Q.addScript("{{Q}}/js/videojs/plugins/YouTube.js", function () {
				var options = {
					techOrder: ["youtube"],
					sources: [{
						src: state.url,
						type: 'video/youtube'
					}],
					youtube: {
						ytControls: 2
					}
				};

				tool.initVideojsPlayer(options);
			});
		}
	};
	tool.adapters.vimeo = {
		init: function () {
			Q.addScript("{{Q}}/js/videojs/plugins/Vimeo.js", function () {
				var options = {
					techOrder: ["vimeo"],
					sources: [{
						src: state.url,
						type: 'video/vimeo'
					}],
					vimeo: {
						ytControls: 2
					}
				};

				tool.initVideojsPlayer(options);
			});
		}
	};
	tool.adapters.twitch = {
		init: function () {
			var element = tool.element;
			var throttle = state.throttle;

			var newA = document.createElement('A');
			newA.href = state.url;
			var host = newA.hostname;

			element.classList.add("Q_video_twitch");

			var options = {
				autoplay: false
				//channel: "<channel ID>",
				//video: "782042263",
				//collection: "<collection ID>"
			};

			// convert start time to pass as option
			if (state.start) {
				options.time = state.start.convertTimeToString(false).replace(/:/, 'h').replace(/:/, 'm') + 's';
			}
			if (state.clipStart) {
				options.time = state.clipStart.convertTimeToString(false).replace(/:/, 'h').replace(/:/, 'm') + 's';
			}

			var videoId = state.url.match(/\/videos\/([0-9]+).*$/);
			var channel = state.url.match(new RegExp(host + "/(\\w+)"));
			if (videoId) {
				options.video = videoId[1];
			} else if (channel) {
				options.channel = channel[1];
			} else {
				throw new Q.Error("Q/video/twitch: none of videoId or channel filled");
			}

			Q.addScript("{{Q}}/js/twitch/lib.js", function () {
				state.player = new Twitch.Player(element, options);
				state.player.currentTime = function (time) {
					state.player.seek(time);
				};

				var _getCurrentPostion = function () {
					return Math.trunc(state.player.getCurrentTime() * 1000);
				};

				var onPlay = Q.throttle(function () {
					state.currentPosition = _getCurrentPostion();
					console.log("Started at position " + state.currentPosition + " milliseconds");
					Q.handle(state.onPlay, tool);

					// clear timer if exist
					tool.clearPlayInterval();

					// start new timer to change needed layout params during play
					state.playIntervalID = setInterval(function(){
						state.currentPosition = _getCurrentPostion();
						Q.handle(state.onPlaying, tool);
					}, 100);
				}, throttle);
				var onPause = Q.throttle(function () {
					var position = _getCurrentPostion();
					console.log("Paused at position " + position + " milliseconds");
					Q.handle(state.onPause, tool);
				}, throttle);
				var onEnded = Q.throttle(function () {
					var position = _getCurrentPostion();
					console.log("Seeked at position " + position + " milliseconds");
					Q.handle(state.onEnded, tool);
				}, throttle);

				state.player.addEventListener(Twitch.Player.PLAY, onPlay);
				state.player.addEventListener(Twitch.Player.PAUSE, onPause);
				state.player.addEventListener(Twitch.Player.ENDED, onEnded);
			});
		}
	};

	//tool.adapters.youtube = tool.adapters.vimeo = tool.adapters.mp4 = tool.adapters.webm = tool.adapters.general;

	var p = Q.pipe(['stylesheet', 'text', 'scripts'], function (params, subjects) {
		tool.text = params.text[1].video;
		tool.implement();
	});

	Q.addStylesheet(["{{Q}}/css/videojs.css", "{{Q}}/css/video.css"], p.fill('stylesheet'), { slotName: 'Q' });
	Q.addScript("{{Q}}/js/videojs/lib.js", p.fill('scripts'));
	Q.Text.get('Q/content', p.fill('text'));
},

{
	url: null,
	autoplay: false,
	throttle: 10,
	currentPosition: 0,
	start: null,
	clipStart: null,
	clipEnd: null,
	videojsOptions: {
		controls: true
	},
	onSuccess: new Q.Event(),
	onError: new Q.Event(function (message) {
		alert('Flie upload error' + (message ? ': ' + message : '') + '.');
	}, 'Q/video'),
	onFinish: new Q.Event(),
	onLoad: new Q.Event(),
	onPlay: new Q.Event(function () {
		this.checkClip();
	}),
	onPlaying: new Q.Event(function () {
		this.checkClip();
	}),
	onPause: new Q.Event(function () {
		this.clearPlayInterval();
	}),
	onSeek: new Q.Event(),
	onEnded: new Q.Event(function () {
		// stop timer if exist
		this.clearPlayInterval();
	})
},

{
	/**
	 * Check clip borders and pause if outside
	 * @method checkClip
	 */
	checkClip: function () {
		var tool = this;
		var state = this.state;

		// clipStart handler
		if (state.clipStart && state.currentPosition < state.clipStart) {
			tool.setCurrentPosition(state.clipStart);
		}
		// clipStart handler
		if (state.clipEnd && state.currentPosition > state.clipEnd) {
			tool.pause();
			tool.setCurrentPosition(state.clipEnd);
		}
	},
	/**
	 * Refreshes the appearance of the tool completely
	 * @method implement
	 */
	implement: function () {
		var tool = this;

		var adapterName = tool.adapterNameFromUrl();
		tool.adapters[adapterName].init();
	},
	/**
	 *
	 * @method initVideojsPlayer
	 */
	initVideojsPlayer: function (options) {
		var tool = this;
		var state = this.state;
		var throttle = state.throttle;

		options = Q.extend(state.videojsOptions, options);

		Q.Template.render('Q/video/videojs', {
			autoplay: state.autoplay ? 'autoplay' : '',
		}, function (err, html) {
			tool.element.innerHTML = html;

			var _getCurrentPostion = function () {
				return Math.trunc(state.player.currentTime() * 1000);
			};

			var onPlay = Q.throttle(function () {
				var position = state.currentPosition || _getCurrentPostion();
				console.log("Started at position " + position + " milliseconds");
				Q.handle(state.onPlay, tool, [position]);
			}, throttle);
			var onPause = Q.throttle(function () {
				var position = state.currentPosition || _getCurrentPostion();
				console.log("Paused at position " + position + " milliseconds");
				Q.handle(state.onPause, tool, [position]);
			}, throttle);
			var onSeek = Q.throttle(function () {
				var position = state.currentPosition || _getCurrentPostion();
				console.log("Seeked at position " + position + " milliseconds");
				Q.handle(state.onSeek, tool, [position]);
			}, throttle);
			var onEnded = Q.throttle(function () {
				var position = state.currentPosition || _getCurrentPostion();
				console.log("Seeked at position " + position + " milliseconds");
				Q.handle(state.onEnded, tool, [position]);
			}, throttle);

			state.player = videojs($("video", tool.element)[0], options, function onPlayerReady() {
				videojs.log('Your player is ready!');

				var start = null;

				if (state.clipStart !== null) {
					start = parseInt(state.clipStart)/1000;
				}
				if (state.start !== null) {
					start = parseInt(state.start)/1000;
				}

				this.currentTime(start);

				this.on('play', function () {
					onPlay();
				});

				this.on('pause', function () {
					onPause();
				});

				this.on('seeking', function() {
					onPause();
				});

				this.on('waiting', function() {
					onPause();
				});

				this.on('seeked', function() {
					onPlay();
					onSeek();
				});

				this.on('ended', function() {
					onPause();
					onEnded();
				});

				// update currentPosition array on play
				this.on('timeupdate', function() {
					setTimeout(function(){
						state.currentPosition = _getCurrentPostion();
						Q.handle(state.onPlaying, tool, [state.currentPosition]);
					}, 500);
				});
			});
		});
	},
	/**
	 * @method play
	 */
	play: function () {
		this.state.player && this.state.player.play();
	},
	/**
	 * @method pause
	 */
	pause: function () {
		this.state.player && this.state.player.pause();
	},
	/**
	 * @method setCurrentPosition
	 * @param {integer} position in milliseonds
	 */
	setCurrentPosition: function (position) {
		this.state.player && this.state.player.currentTime(position/1000);
	},
	/**
	 * @method getCurrentPosition
	 */
	getCurrentPosition: function () {
		return this.state.currentPosition;
	},
	/**
	 * Detect adapter from url
	 * @method adapterNameFromUrl
	 */
	adapterNameFromUrl: function () {
		var url = this.state.url;
		if (!url) {
			throw new Q.Exception(this.id + ": url is required");
		}
		var newA = document.createElement('A');
		newA.href = url;
		var host = newA.hostname;

		if (host.includes("youtube.com") || host.includes("youtu.be")) {
			return 'youtube';
		} else if (host.includes("vimeo.com")) {
			return 'vimeo';
		} else if (host.includes("twitch")) {
			return 'twitch';
		}

		var ext = url.split('.').pop();
		switch(ext) {
			case 'webm': return 'webm';
			case 'ogg': return 'ogg';
			default: return 'mp4';
		}
		//throw new Q.Exception(this.id + ': No adapter for this URL');
	},
	/**
	 *
	 * @method clearPlayInterval
	 */
	clearPlayInterval: function () {
		var playIntervalID = this.state.playIntervalID;
		if (playIntervalID) {
			clearInterval(playIntervalID);
			playIntervalID = false;
		}
	},
	Q: {
		beforeRemove: function () {
			this.clearPlayInterval();

			// if videojs, call dispose to kill this player with events, triggers, dom etc
			if (Q.getObject("player.dispose", this.state)) {
				this.state.player.dispose();
			}
		}
	}
});

Q.Template.set('Q/video/videojs',
	'<video preload="auto" controls class="video-js vjs-default-skin vjs-4-3" width="100%" height="auto" {{autoplay}}/>'
);

})(window, Q, jQuery);<|endoftext|>


<commit_before>/*
 * grunt-angular-service
 * https://github.com/obibring/grunt-angular-service
 *
 * Copyright (c) 2013 Orr Bibring
 * Licensed under the MIT license.
 */

 /*
 * 1. Read libraries.
 * 2. Check if libraries interact with global namespace (warn / throw).
 * 3.
 */
'use strict';
var _ = require('underscore');

// Template that wraps JavaScript in angular factory definition.
var template = _.template("(function(angular) {"+
  "  angular.module('<%= module %>')" +
  "    .factory('<%= factory %>', function() {" +
  "      function temp() {" +
  "        <%= src %>" +
  "      }" +
  "      var lib = new temp();" +
  "      var properties = [];" +
  "      for (var key in lib) { " +
  "        if (lib.hasOwnProperty(key)) properties.push(key);" +
  "      }" +
  "      if (properties.length === 1) {" +
  "        return lib[properties.pop()];" +
  "       } else {" +
  "         return lib;" +
  "       } " +
  "     });" +
  "  })(window.angular);");

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('ngservice',
    'Generate AngularJS services from JavaScript libraries', function() {

        var warn = function (message) {
          grunt.log.warn(this.name + ": " + message);
        }.bind(this);

        var log = function (message) {
          grunt.log.writeln(this.name + ": " + message);
        }.bind(this);

      // Merge task-specific and/or target-specific options with these defaults.
      var options = this.options({
        punctuation: '.',
        separator: ', '
      });

      // Must provide name of angular module to be created.
      //this.requiresConfig(this.name+'.module');
      //this.requiresConfig(this.name+'.factory');

      var module = this.data.module;
      var factory = this.data.factory;

      console.log(this.files);

      // Iterate over all specified file groups.
      this.files.forEach(function(f) {
        var src = f.src.filter(function(filepath) {
          // Warn on and remove invalid source files (if nonull was set).
          if (!grunt.file.exists(filepath)) {
            warn('Source file "' + filepath + '" not found.');
            return false;
          } else {
            return true;
          }
        }).map(function(filepath) {
          // Read file source.
          return grunt.file.read(filepath);
        }).forEach(function(srcString) {
          var service = template({
            src: srcString,
            module: module,
            factory: factory
          });

          // Write the destination file.
          grunt.file.write(f.dest, service);

          // Print a success message.
          log('File "' + f.dest + '" created.');
        });

    });
  });

};
<commit_msg>Add module definition and dependency support<commit_after>/*
 * grunt-angular-service
 * https://github.com/obibring/grunt-angular-service
 *
 * Copyright (c) 2013 Orr Bibring
 * Licensed under the MIT license.
 */

 /*
 * 1. Read libraries.
 * 2. Check if libraries interact with global namespace (warn / throw).
 * 3.
 */
'use strict';
var _ = require('underscore');

var quoteWrap = function (dep) {
  // Wrap dependency in quotes.
  return "'" + dep + "'";
};

// Template that wraps JavaScript in angular factory definition.
//
var makeTemplate = function(define, dependencies) {

  var deps = dependencies || [];
  var defineStr = define ? ', [' + deps.map(quoteWrap).join(', ') + ']' : '';
  var srvcStr = deps.length ? deps.map(quoteWrap).join(', ') + ', ' : '';

  var template =
    "(function(angular) {"+
    "  angular.module('<%= module %>'" + defineStr +  ")" +
    "    .factory('<%= service %>', [" + srvcStr + "function("+ deps.join(', ') +") {" +
    "      function temp() {" +
    "        <%= src %>" +
    "      }";

  // Above, `src` may have dependencies. Thus, prior to executing `temp`
  // create an execution context injected with these dependencies.
  template += " var context = {}; ";

  // Keep track of which dependencies were injected into `context`.
  template += " var injected = {}; ";

  deps.forEach(function(dep){
    template += "context['" + dep + "'] = " + dep + ";";
    template += "injected['" + dep + "'] = true;";
  });

  template +=
    "      temp.call(context);" +
    // Iterate over properties of `context`, comparing each property to the
    // properties injected above. If only a single property exists that
    // wasn't injected, this sole property is returned.
    "      var addedProps = [];" +
    "      for (var prop in context) { " +
    "        if (context.hasOwnProperty(prop) && !injected.hasOwnProperty(prop)) { " +
    "           addedProps.push(prop);" +
    "        } " +
    "      }" +
    "      if (addedProps.length === 1) {" +
    "        return context[addedProps.pop()];" +
    "       } else {" +
    // `src` added more than a single property into `context`. Make no
    // assumptions and return the entire `context` object.
    "         return context;" +
    "       } " +
    "     }]);" +
    "  })(window.angular);";

  return template;
};

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('ngservice',
    'Generate AngularJS services from JavaScript libraries', function() {

        var warn = function (message) {
          grunt.log.warn(this.name + ": " + message);
        }.bind(this);

        var log = function (message) {
          grunt.log.writeln(this.name + ": " + message);
        }.bind(this);

        var fatal = function (message) {
          grunt.fatal(this.name + ": " + message);
        }.bind(this);

      var data = this.data;

      // Determine validity of `module` option.
      if (!_.has(data, 'module') || !_.isString(data.module)) {
        fatal('Invalid `module` option.');
      }

      // Determine validity of `service` option.
      if (!_.has(data, 'service') || !_.isString(data.service)) {
        fatal('Invalid `service` option.');
      }

      // Determine validity of `define` option.
      if (_.has(data, 'define') && !_.isBoolean(data.define)) {
        fatal('Invalid `define` option.');
      }

      var module  = data.module;                  // Name of module to be written.
      var dependencies = data.dependencies || []; // Service dependencies.
      var define  = data.define || false;         // Whether to define the module.
      var service = data.service;                 // Name of service to be written.

      var sources = [];

      // Iterate over all specified file groups.
      this.files.forEach(function(f) {
        var src = f.src.filter(function(filepath) {
          // Warn on and remove invalid source files (if nonull was set).
          if (!grunt.file.exists(filepath)) {
            warn('Source file "' + filepath + '" not found.');
            return false;
          } else {
            return true;
          }
        }).map(function(filepath) {
          // Read file source.
          return grunt.file.read(filepath);
        }).forEach(function(srcString) {
          sources.push(srcString);
        });

        // If sources is empty, die.
        if (sources.length < 1) {
          fatal('No source files provided.');
        }

        var template = makeTemplate(define, dependencies);

        // Write the destination file.
        grunt.file.write(f.dest, _.template(template, {
          src: sources.join("\n"),
          module: module,
          service: service
        }));

        // Print a success message.
        log('File "' + f.dest + '" created.');

    });
  });

};
<|endoftext|>


<commit_before>/*
 * Pekoe Workspace provides a wrapper for the Pekoe Job Manager
 * Copyright (C) 2009,2010,2011-2014 Geordie Springfield Pty Ltd (Australia)
 * Author: Alister Pillow alisterhp@me.com

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 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 the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var gs;
if (!gs) {
    gs = {};
}
//gs.cookies = document.cookie.split('; ');
/*
 Leaving this ... code behind to remind me that Auth isn't right yet for this content. Still might want to use it here.
 Alternatives are:
 - make this an angular page (ugh!)
 - have a standard FORM which can be loaded (instead of the 401 page)
 */
//gs.service = (function (){
//    var s = {};
//
//    if (window.parent !== window) { // must be a child frame
//        s = window.parent.AuthService;
//    } else {
//        s.getTenant = function () {
//            return document.cookie;
//        }
//    }
//    // console.log('service is',s);
//    // who uses it?
//    return s;
//})();

/*
 If the tabService is available, hijack clicks and send the Text and href to the tabService, otherwise allow default.
 If the TARGET is not _self, then load the page in a new tab.
 If the Command/Control key is down, then load the page in a new Tab - regardless of TARGET
 */

$(function () {
    if (window.parent !== window) { // this must be a child frame - not a standalone page
        gs.angular = window.parent.angular;
        gs.scope = gs.angular.element(window.frameElement).scope();
        // gs.scope has access to the functions of the tabs.ctrl.js
        // that gives me access to the tabs!
    } else {
        //console.log('standalone page - anchors behave normally');
    }

    var controlsForSelection = $('.p-needs-selection').attr('disabled', true);
    var updateControls = function () {
        controlsForSelection.removeAttr('disabled');
    };
    // generic menu handler. Each action should be an "a href" with data-action=unlock and possibly data-params=item,item data-confirm=yes
    // I might change GET for POST.
    // each action should return a redirect so that the action can't be repeated.
    $('.menuitem').on('click',function (e){
        e.preventDefault();
        //var action = $(this).text().toLowerCase(); // or what about the url?
        var $this = $(this);
        var href = $this.attr('href');
        var params = $this.data('params');
        var action = $this.data('action');
        var $tr = activeItem();
        var item = tabInfo($tr);
        var thePath = item.path;
        var confirmationRequired = $this.data('confirm'); // this could be a question
        if (confirmationRequired) {
            if (!confirm('Are you sure you want to ' + action + ' the file ' + thePath)) {return true;}
        }
        var data = {action: action, path: thePath};

        console.log('Going to ',action, data.path);
        $tr.find('td:first').html('');
        //if (action === 'delete' && !confirm("Are you sure?")) return true;
        console.log('going to href',href, 'with params', $.param(data));
        location.href = href + '?' + $.param(data);

        //$.get(href, data).then(function (data, textStatus, jqXHR) {
        //    if (jqXHR.getResponseHeader('Location')) {
        //        location.href = jqXHR.getResponseHeader('Location');
        //    }
        //});
        //
    });

    $('#refresh').on('click', function () {
        location.reload();
    });

    var activeItem = function () {
        return $($('.active')[0]);
    };

    var tabInfo = function ($tr) {
        var tab = {};
        tab.href = $tr.data('href');
        tab.title = $tr.data('title');
        tab.type = $tr.data('type');
        tab.path = $tr.data('path');
        //tab.class = $tr.class();
        var params = $tr.data('params');
        if (params) {tab.params = params};
        return tab;
    };

    // default openItem for standalone page
    var openItem = function (tab) { // open in this window
        location.href = tab.href;
    };

    //// all the 'p-needs-selection' buttons should really do a get with their name as action
    //$('unlockItem').on("click",function () {
    //    var data = {action: 'unlock'};
    //    var item = tabInfo(activeItem());
    //    data.path = item.path;
    //    console.log('Going to unlock ', data.path);
    //    $.get(location.pathname, data).then(function (data, textStatus, jqXHR) {
    //        if (jqXHR.getResponseHeader('Location')) {
    //            location.href = jqXHR.getResponseHeader('Location');
    //        }
    //    });
    //});

    // Buttons
    $('#openItem').on('click', function (e) {
        openItem(tabInfo(activeItem()), e.metaKey);
    });

    $('#openItemTab').on('click', function () {
        openItem(tabInfo(activeItem()), true);
    });

    $('#bookmarkItem').on('click', function () {

    });
    //$('#deleteItem').on('click', function () {
    //    var data = {action: 'delete'};
    //    var item = tabInfo(activeItem());
    //    data.path = item.path;
    //    console.log('Going to delete ', data.path);
    //    $.post(location.pathname, data).then(function (data, textStatus, jqXHR) {
    //        if (jqXHR.getResponseHeader('Location')) {
    //            location.href = jqXHR.getResponseHeader('Location');
    //        }
    //    });
    //});

    // row-click actions
    $('tr[data-href]')
        .on('click', function (e) {
            e.preventDefault();
            $('.active').removeClass('active');
            updateControls();
            $(this).addClass('active');
        })
        .on('dblclick', function (e) {
            e.preventDefault();
            var tab = tabInfo(activeItem());
            openItem(tabInfo(activeItem()), e.metaKey);
            return false;
        });

    // when in child frame of Pekoe Workspace
    if (gs.scope) { // Can only create a bookmark if this is in an iframe. Can only create a new Pekoe-tab if in Pekoe-workspace
        // override definition
        var openItem = function (tab, inNewTab) {
            console.log('gs.scope.openitem tab.type',tab.type);
            if (tab.type === 'form' || inNewTab) {
                gs.scope.addTab(tab); //
            } else if (tab.type === 'other' && tab.href.indexOf('odt:') == 0) { // TODO ------------ tidy up...
                location.href = tab.href.split(":")[1];
            } else if (tab.type === 'other' && tab.href.indexOf('docx:') == 0) {
                location.href = tab.href.split(":")[1];
            }
             else {
                location.href = tab.href;
            }
        };

        $('tr.collection').on('dragenter',function(e){
            e.preventDefault();
            $(this).css('outline','solid red 1px');
            //console.log('dragenter');
            //console.log('thisis',this);
//            $(e.target).background('red');
        }).on('dragover',function(e){
            e.originalEvent.dataTransfer.dropEffect='move';
            e.originalEvent.preventDefault();
            //console.log('dragover')
        }).on('drop',function(e){
            console.log('you dropped', e.originalEvent.target, 'onto',this); // you dropped td onto tr
            // now send a move event using the data.
            var data = e.originalEvent.dataTransfer.getData('application/json');
            console.log('want to move ',data, 'into',$(this).data('href'));
            $(this).css('outline','');
        }).on('dragleave',function () {
            $(this).css('outline','');
        });
        /*
         function onDrop(event)
         {
         var data = event.dataTransfer.getData("text/plain");
         event.target.textContent = data;
         event.preventDefault();
         }
         */

        // NOTE: this is the HTML5 Drag and Drop API, NOT jQueryUI
        $('*[data-href]')
            .attr('draggable', true).on('dragstart', function (ev) {
                console.log('ds');
                ev.originalEvent.dataTransfer.effectAllowed="move";
                ev.originalEvent.dataTransfer.setData('application/json', gs.angular.toJson(tabInfo($(ev.target)))); // Lovely - it works. Drag to Bookmarks in main frame.
            });
    }

    $('table')

});

<commit_msg>when page loads, it updates its parent frame tab with the new href. Allows bookmarking of Searches and current view. cleaned up console messages.<commit_after>/*
 * Pekoe Workspace provides a wrapper for the Pekoe Job Manager
 * Copyright (C) 2009,2010,2011-2014 Geordie Springfield Pty Ltd (Australia)
 * Author: Alister Pillow alisterhp@me.com

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 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 the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var gs;
if (!gs) {
    gs = {};
}
//gs.cookies = document.cookie.split('; ');
/*
 Leaving this ... code behind to remind me that Auth isn't right yet for this content. Still might want to use it here.
 Alternatives are:
 - make this an angular page (ugh!)
 - have a standard FORM which can be loaded (instead of the 401 page)
 */
//gs.service = (function (){
//    var s = {};
//
//    if (window.parent !== window) { // must be a child frame
//        s = window.parent.AuthService;
//    } else {
//        s.getTenant = function () {
//            return document.cookie;
//        }
//    }
//    // console.log('service is',s);
//    // who uses it?
//    return s;
//})();

/*
 If the tabService is available, hijack clicks and send the Text and href to the tabService, otherwise allow default.
 If the TARGET is not _self, then load the page in a new tab.
 If the Command/Control key is down, then load the page in a new Tab - regardless of TARGET
 */

$(function () {
    if (window.parent !== window) { // this must be a child frame - not a standalone page
        gs.angular = window.parent.angular;
        gs.scope = gs.angular.element(window.frameElement).scope();
        // gs.scope has access to the functions of the tabs.ctrl.js
        // that gives me access to the tabs!
    } else {
        //console.log('standalone page - anchors behave normally');
    }

    var controlsForSelection = $('.p-needs-selection').attr('disabled', true);
    var updateControls = function () {
        controlsForSelection.removeAttr('disabled');
    };
    // generic menu handler. Each action should be an "a href" with data-action=unlock and possibly data-params=item,item data-confirm=yes
    // I might change GET for POST.
    // each action should return a redirect so that the action can't be repeated.
    $('.menuitem').on('click',function (e){
        e.preventDefault();
        //var action = $(this).text().toLowerCase(); // or what about the url?
        var $this = $(this);
        var href = $this.attr('href');
        var params = $this.data('params');
        var action = $this.data('action');
        var $tr = activeItem();
        var item = tabInfo($tr);
        var thePath = item.path;
        var confirmationRequired = $this.data('confirm'); // this could be a question
        if (confirmationRequired) {
            if (!confirm('Are you sure you want to ' + action + ' the file ' + thePath)) {return true;}
        }
        var data = {action: action, path: thePath};

        //console.log('Going to ',action, data.path);
        $tr.find('td:first').html('');
        //if (action === 'delete' && !confirm("Are you sure?")) return true;
        //console.log('going to href',href, 'with params', $.param(data));
        location.href = href + '?' + $.param(data);

        //$.get(href, data).then(function (data, textStatus, jqXHR) {
        //    if (jqXHR.getResponseHeader('Location')) {
        //        location.href = jqXHR.getResponseHeader('Location');
        //    }
        //});
        //
    });

    $('#refresh').on('click', function () {
        location.reload();
    });

    var activeItem = function () {
        return $($('.active')[0]);
    };

    var tabInfo = function ($tr) {
        var tab = {};
        tab.href = $tr.data('href');
        tab.title = $tr.data('title');
        tab.type = $tr.data('type');
        tab.path = $tr.data('path');
        //tab.class = $tr.class();
        var params = $tr.data('params');
        if (params) {tab.params = params};
        return tab;
    };

    // default openItem for standalone page
    var openItem = function (tab) { // open in this window
        location.href = tab.href;
    };

    //// all the 'p-needs-selection' buttons should really do a get with their name as action
    //$('unlockItem').on("click",function () {
    //    var data = {action: 'unlock'};
    //    var item = tabInfo(activeItem());
    //    data.path = item.path;
    //    console.log('Going to unlock ', data.path);
    //    $.get(location.pathname, data).then(function (data, textStatus, jqXHR) {
    //        if (jqXHR.getResponseHeader('Location')) {
    //            location.href = jqXHR.getResponseHeader('Location');
    //        }
    //    });
    //});

    // Buttons
    $('#openItem').on('click', function (e) {
        openItem(tabInfo(activeItem()), e.metaKey);
    });

    $('#openItemTab').on('click', function () {
        openItem(tabInfo(activeItem()), true);
    });

    $('#bookmarkItem').on('click', function () {

    });
    //$('#deleteItem').on('click', function () {
    //    var data = {action: 'delete'};
    //    var item = tabInfo(activeItem());
    //    data.path = item.path;
    //    console.log('Going to delete ', data.path);
    //    $.post(location.pathname, data).then(function (data, textStatus, jqXHR) {
    //        if (jqXHR.getResponseHeader('Location')) {
    //            location.href = jqXHR.getResponseHeader('Location');
    //        }
    //    });
    //});

    // row-click actions
    $('tr[data-href]')
        .on('click', function (e) {
            e.preventDefault();
            $('.active').removeClass('active');
            updateControls();
            $(this).addClass('active');
        })
        .on('dblclick', function (e) {
            e.preventDefault();
            var tab = tabInfo(activeItem());
            openItem(tabInfo(activeItem()), e.metaKey);
            return false;
        });

    // when in child frame of Pekoe Workspace
    if (gs.scope) { // Can only create a bookmark if this is in an iframe. Can only create a new Pekoe-tab if in Pekoe-workspace
        // override definition

        gs.scope.tab.href = location.href; // Lovely. I could even update the title if I want!

        var openItem = function (tab, inNewTab) {
            if (tab.type === 'form' || inNewTab) {
                gs.scope.addTab(tab); //
            } else if (tab.type === 'other' && tab.href.indexOf('odt:') == 0) { // TODO ------------ tidy up...
                location.href = tab.href.split(":")[1];
            } else if (tab.type === 'other' && tab.href.indexOf('docx:') == 0) {
                location.href = tab.href.split(":")[1];
            }
             else {
                location.href = tab.href;
            }
        };

        $('tr.collection').on('dragenter',function(e){
            e.preventDefault();
            $(this).css('outline','solid red 1px');

        }).on('dragover',function(e){
            e.originalEvent.dataTransfer.dropEffect='move';
            e.originalEvent.preventDefault();
        }).on('drop',function(e){
            // now send a move event using the data.
            var data = e.originalEvent.dataTransfer.getData('application/json');
            $(this).css('outline','');
        }).on('dragleave',function () {
            $(this).css('outline','');
        });
        /*
         function onDrop(event)
         {
         var data = event.dataTransfer.getData("text/plain");
         event.target.textContent = data;
         event.preventDefault();
         }
         */

        // NOTE: this is the HTML5 Drag and Drop API, NOT jQueryUI
        $('*[data-href]')
            .attr('draggable', true).on('dragstart', function (ev) {
                ev.originalEvent.dataTransfer.effectAllowed="move";
                ev.originalEvent.dataTransfer.setData('application/json', gs.angular.toJson(tabInfo($(ev.target)))); // Lovely - it works. Drag to Bookmarks in main frame.
            });
    }

});

<|endoftext|>


<commit_before>'use strict';

// Polyfill for requestAnimationFrame() / cancelAnimationFrame()
(function() {
  var lastTime = 0;
  var vendors = ['webkit', 'moz', 'ms', 'o'];

  for (var i = 0; i < vendors.length && !window.requestAnimationFrame; i++) {
    window.requestAnimationFrame = window[vendors[i] + 'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vendors[i] + 'CancelAnimationFrame'] ||
      window[vendors[i] + 'RequestCancelAnimationFrame'];
  }

  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = function(callback, element) {
      var currTime = Date.now();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = window.setTimeout(function() {
        callback(currTime + timeToCall);
      }, timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };
  }

  if (!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = function(id) {
      window.clearTimeout(id);
    };
  }
})();

var SKScrollEventType = {
  ScrollStart: 'SKScrollStart',
  ScrollEnd: 'SKScrollEnd'
};

var SKScrollBarType = {
  Horizontal: 0,
  Vertical: 1
};

var SKScrollView = function(element) {
  var kBounceTransitionDuration = 0.35;
  var kAccelerationTimeout = 250;
  var kAcceleration = 15;
  var kElasticDeceleration = 0.03;
  var kElasticAcceleration = 0.08;
  var kMinimumDecelerationVelocity = 1;
  var kDecelerationFactor = 0.95;
  var kMinimumVelocity = 0.01;
  var kScrollBarHideTimeout = 100;

  var _isDragging = false;
  var _isDecelerating = false;
  var _isScrolling = false;
  var _startAccelerateX = 0;
  var _startAccelerateY = 0;
  var _lastMouseX = -1;
  var _lastMouseY = -1;
  var _lastTimeStamp = Date.now();
  var _decelerationAnimationInterval = null;

  var $window = $(window['addEventListener'] ? window : document.body);
  var $element = this.$element = $(element);
  element = this.element = $(element).get(0);

  var content = this.content = new SKScrollContent(this);
  var horizontalScrollBar = this.horizontalScrollBar = new SKScrollBar(this, SKScrollBarType.Horizontal);
  var verticalScrollBar = this.verticalScrollBar = new SKScrollBar(this, SKScrollBarType.Vertical);
  var self = this;

  var startDeceleration = function(startTime) {
    var acceleration = (startTime - _lastTimeStamp) / kAcceleration;
    var accelerateDeltaX = self.x - _startAccelerateX;
    var accelerateDeltaY = self.y - _startAccelerateY;
    var velocityX = accelerateDeltaX / acceleration;
    var velocityY = accelerateDeltaY / acceleration;
    var minimumX = self.minimumX;
    var minimumY = self.minimumY;
    var elasticDeceleration = kElasticDeceleration;
    var elasticAcceleration = kElasticAcceleration;
    var lastFrameTime = 0;

    var stepAnimation = function (currentFrameTime) {
      if (!_isDecelerating) return;

      var deltaTime = currentFrameTime - lastFrameTime;
      var x = self.x = self.x + (!self.canScrollHorizontal() ? 0 : velocityX);
      var y = self.y = self.y + (!self.canScrollVertical() ? 0 : velocityY);

      velocityX *= kDecelerationFactor;
      velocityY *= kDecelerationFactor;

      if (Math.abs(velocityX) <= kMinimumVelocity &&
          Math.abs(velocityY) <= kMinimumVelocity) {
        _isDecelerating = false;
        _isScrolling = false;
        horizontalScrollBar.hide();
        verticalScrollBar.hide();
        $element.trigger(SKScrollEventType.ScrollEnd);
        return;
      }

      content.translate(x, y);
      _decelerationAnimationInterval = window.requestAnimationFrame(stepAnimation);

      var elasticX = (x < minimumX) ? minimumX - x : (x > 0) ? -x : 0;
      var elasticY = (y < minimumY) ? minimumY - y : (y > 0) ? -y : 0;

      if (elasticX) velocityX = (elasticX * velocityX <= 0) ?
        velocityX + (elasticX * elasticDeceleration) : elasticX * elasticAcceleration;

      if (elasticY) velocityY = (elasticY * velocityY <= 0) ?
        velocityY + (elasticY * elasticDeceleration) : elasticY * elasticAcceleration;

      lastFrameTime = currentFrameTime;
    };

    if (Math.abs(velocityX) > kMinimumDecelerationVelocity ||
        Math.abs(velocityY) > kMinimumDecelerationVelocity) {
      _isDecelerating = true;
      _decelerationAnimationInterval = window.requestAnimationFrame(stepAnimation);
      lastFrameTime = Date.now();
    } else {
      _isScrolling = false;
      horizontalScrollBar.hide();
      verticalScrollBar.hide();
      $element.trigger(SKScrollEventType.ScrollEnd);
    }
  };

  var stopDeceleration = function() {
    _isDecelerating = false;
    window.cancelAnimationFrame(_decelerationAnimationInterval);
  };

  var bounceTransitionEndHandler = function(evt) {
    $element.unbind(evt);
    _isScrolling = false;
    horizontalScrollBar.hide();
    verticalScrollBar.hide();
    $element.trigger(SKScrollEventType.ScrollEnd);
  };

  $element.bind('mousedown touchstart', function(evt) {
    var scrollViewSize = self.getSize();
    var contentSize = content.getSize();
    var x = _startAccelerateX = self.x;
    var y = _startAccelerateY = self.y;
    var minimumX = self.minimumX = scrollViewSize.width - contentSize.width;
    var minimumY = self.minimumY = scrollViewSize.height - contentSize.height;

    _isDragging = true;
    _lastMouseX = (evt.type === 'touchstart') ? evt.originalEvent.touches[0].pageX : evt.pageX;
    _lastMouseY = (evt.type === 'touchstart') ? evt.originalEvent.touches[0].pageY : evt.pageY;
    _lastTimeStamp = evt.timeStamp;

    stopDeceleration();

    if (self.canScrollHorizontal()) {
      horizontalScrollBar.show();
      horizontalScrollBar.update(true);
    }

    if (self.canScrollVertical()) {
      verticalScrollBar.show();
      verticalScrollBar.update(true);
    }

    evt.preventDefault();
  });

  $window.bind('mousemove touchmove', function(evt) {
    if (!_isDragging) return;

    var mouseX = (evt.type === 'touchmove') ? evt.originalEvent.touches[0].pageX : evt.pageX;
    var mouseY = (evt.type === 'touchmove') ? evt.originalEvent.touches[0].pageY : evt.pageY;
    var deltaX = mouseX - _lastMouseX;
    var deltaY = mouseY - _lastMouseY;
    var minimumX = self.minimumX;
    var minimumY = self.minimumY;
    var x = self.x + deltaX;
    var y = self.y + deltaY;

    x -= ((x < minimumX) ? deltaX : (x > 0) ? deltaX : 0) / 2;
    y -= ((y < minimumY) ? deltaY : (y > 0) ? deltaY : 0) / 2;

    self.x = !self.canScrollHorizontal() ? 0 : x;
    self.y = !self.canScrollVertical() ? 0 : y;

    content.translate(self.x, self.y);

    var accelerationTime = evt.timeStamp - _lastTimeStamp;

    if (accelerationTime > kAccelerationTimeout) {
      _startAccelerateX = self.x;
      _startAccelerateY = self.y;
      _lastTimeStamp = evt.timeStamp;
    }

    if (self.canScrollHorizontal()) horizontalScrollBar.update();
    if (self.canScrollVertical()) verticalScrollBar.update();

    if (!_isScrolling) {
      _isScrolling = true;
      $element.trigger(SKScrollEventType.ScrollStart);
    }

    _lastMouseX = mouseX;
    _lastMouseY = mouseY;
  });

  $window.bind('mouseup touchend', function(evt) {
    if (!_isDragging) return;

    var timeStamp = evt.timeStamp;
    var accelerationTime = timeStamp - _lastTimeStamp;
    var minimumX = self.minimumX;
    var minimumY = self.minimumY;
    var x = Math.min(Math.max(minimumX, self.x), 0);
    var y = Math.min(Math.max(minimumY, self.y), 0);

    var bounce = function() {
      $element.bind('webkitTransitionEnd transitionend MSTransitionEnd oTransitionEnd transitionEnd', bounceTransitionEndHandler);
      content.translate(x, y, kBounceTransitionDuration);
      self.x = _startAccelerateX = !self.canScrollHorizontal() ? 0 : x;
      self.y = _startAccelerateX = !self.canScrollVertical() ? 0 : y;
    };

    if (accelerationTime < kAccelerationTimeout) {
      if (x !== self.x || y !== self.y) {
        bounce();
      } else {
        startDeceleration(timeStamp);
      }
    } else if (x !== self.x || y !== self.y) {
      bounce();
    } else {
      _isScrolling = false;
      horizontalScrollBar.hide();
      verticalScrollBar.hide();
      $element.trigger(SKScrollEventType.ScrollEnd);
    }

    _isDragging = false;
    _lastMouseX = -1;
    _lastMouseY = -1;
    _lastTimeStamp = timeStamp;
  });

  $element.data('scrollView', this);
};

SKScrollView.prototype = {
  minimumX: 0,
  minimumY: 0,
  element: null,
  $element: null,
  content: null,
  horizontalScrollBar: null,
  verticalScrollBar: null,
  x: 0,
  y: 0,
  canScrollHorizontal: function() { return (this.minimumX < 0); },
  canScrollVertical: function() { return (this.minimumY < 0); },
  getSize: function() {
    var $element = this.$element;
    return { width: $element.width(), height: $element.height() };
  }
};

var SKScrollContent = function(scrollView) {
  this.scrollView = scrollView;
  scrollView.$element.wrapInner('
');

  var $element = this.$element = scrollView.$element.children('.sk-scroll-content');
  var element = this.element = $element.get(0);
};

SKScrollContent.prototype = {
  element: null,
  $element: null,
  scrollView: null,
  getSize: function() {
    var $element = this.$element;
    return { width: $element.width(), height: $element.height() };
  },
  translate: function(x, y, duration) {
    var translate3d = 'translate3d(' + x + 'px, ' + y + 'px, 0)';
    var translate = 'translate(' + x + 'px, ' + y + 'px)';

    duration = (duration) ? duration + 's' : '0s';

    this.$element.css({
      '-webkit-transition-duration': duration,
      '-moz-transition-duration': duration,
      '-ms-transition-duration': duration,
      '-o-transition-duration': duration,
      'transition-duration': duration,
      '-webkit-transform': translate3d,
      '-moz-transform': translate,
      '-ms-transform': translate,
      '-o-transform': translate,
      'transform': translate
    });

    var scrollView = this.scrollView;

    if (scrollView.canScrollHorizontal()) scrollView.horizontalScrollBar.update();
    if (scrollView.canScrollVertical()) scrollView.verticalScrollBar.update();
  }
};

var SKScrollBar = function(scrollView, type) {
  this.scrollView = scrollView;
  this.type = type;

  var $element = this.$element = $('
');
  var element = this.element = $element.get(0);

  scrollView.$element.append($element);

  if (this.type === SKScrollBarType.Horizontal) {
    this.width = parseInt($element.css('height'), 10);
    $element.css({ 'bottom': '0', 'left': '0' });
  } else {
    this.width = parseInt($element.css('width'), 10);
    $element.css({ 'top': '0', 'right': '0' });
  }
};

SKScrollBar.prototype = {
  _size: 0,
  _scrollViewSize: null,
  _contentSize: null,
  element: null,
  $element: null,
  scrollView: null,
  type: SKScrollBarType.Horizontal,
  minimumSize: 34,
  width: 5,
  setSize: function(value) {
    this._size = value;
    this.$element.css((this.type === SKScrollBarType.Horizontal) ? 'width' : 'height', value + 'px');
  },
  update: function(recalculateSize) {
    var scrollView = this.scrollView;
    var minimumSize = this.minimumSize;
    var width = this.width;
    var type = this.type;
    var content = scrollView.content;
    var scrollViewSize, contentSize

    if (recalculateSize) {
      scrollViewSize = this._scrollViewSize = scrollView.getSize();
      contentSize = this._contentSize = content.getSize();
    } else {
      scrollViewSize = this._scrollViewSize;
      contentSize = this._contentSize;
    }

    var canScrollHorizontal = scrollView.canScrollHorizontal();
    var canScrollVertical = scrollView.canScrollVertical();
    var margin, size, scrollPosition, minimumPosition, position, translate3d, translate;

    if (type === SKScrollBarType.Horizontal) {
      margin = (canScrollVertical ? width * 2 : width) + 1;
      scrollPosition = scrollView.x;
      size = (!recalculateSize && minimumPosition < scrollPosition && scrollPosition < 0) ?
        this._size : Math.max(minimumSize, Math.round(
          (scrollViewSize.width / contentSize.width) * (scrollViewSize.width - margin)
        ));

      minimumPosition = scrollView.minimumX;
      position = (scrollPosition / minimumPosition) * (scrollViewSize.width - margin - size);

      if (scrollPosition > 0) {
        size = Math.round(Math.max(size - scrollPosition, width));
        position = 1;
      } else if (scrollPosition < minimumPosition) {
        size = Math.round(Math.max(size - minimumPosition + scrollPosition, width));
        position = scrollViewSize.width - size - margin;
      }

      translate3d = 'translate3d(' + position + 'px, 0, 0)';
      translate = 'translate(' + position + 'px, 0)';
    } else {
      margin = (canScrollHorizontal ? width * 2 : width) + 1;
      scrollPosition = scrollView.y;
      size = (!recalculateSize && minimumPosition < scrollPosition && scrollPosition < 0) ?
        this._size : Math.max(minimumSize, Math.round(
          (scrollViewSize.height / contentSize.height) * (scrollViewSize.height - margin)
        ));

      minimumPosition = scrollView.minimumY;
      position = (scrollPosition / minimumPosition) * (scrollViewSize.height - margin - size);

      if (scrollPosition > 0) {
        size = Math.round(Math.max(size - scrollPosition, width));
        position = 1;
      } else if (scrollPosition < minimumPosition) {
        size = Math.round(Math.max(size - minimumPosition + scrollPosition, width));
        position = scrollViewSize.height - size - margin;
      }

      translate3d = 'translate3d(0, ' + position + 'px, 0)';
      translate = 'translate(0, ' + position + 'px)';
    }

    if (this._size !== size) this.setSize(size);

    this.$element.css({
      '-webkit-transform': translate3d,
      '-moz-transform': translate,
      '-ms-transform': translate,
      '-o-transform': translate,
      'transform': translate
    });
  },
  show: function() {
    this.$element.addClass('active');
  },
  hide: function() {
    this.$element.removeClass('active');
  }
};

$(function() {
  $('.sk-scroll-view').each(function(index, element) { new SKScrollView(element); });
});
<commit_msg>Rounded scroll position to integer values when scrolling ends to increase the sharpness of rendered text.<commit_after>'use strict';

// Polyfill for requestAnimationFrame() / cancelAnimationFrame()
(function() {
  var lastTime = 0;
  var vendors = ['webkit', 'moz', 'ms', 'o'];

  for (var i = 0; i < vendors.length && !window.requestAnimationFrame; i++) {
    window.requestAnimationFrame = window[vendors[i] + 'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vendors[i] + 'CancelAnimationFrame'] ||
      window[vendors[i] + 'RequestCancelAnimationFrame'];
  }

  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = function(callback, element) {
      var currTime = Date.now();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = window.setTimeout(function() {
        callback(currTime + timeToCall);
      }, timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };
  }

  if (!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = function(id) {
      window.clearTimeout(id);
    };
  }
})();

var SKScrollEventType = {
  ScrollStart: 'SKScrollStart',
  ScrollEnd: 'SKScrollEnd'
};

var SKScrollBarType = {
  Horizontal: 0,
  Vertical: 1
};

var SKScrollView = function(element) {
  var kBounceTransitionDuration = 0.35;
  var kAccelerationTimeout = 250;
  var kAcceleration = 15;
  var kElasticDeceleration = 0.03;
  var kElasticAcceleration = 0.08;
  var kMinimumDecelerationVelocity = 1;
  var kDecelerationFactor = 0.95;
  var kMinimumVelocity = 0.01;
  var kScrollBarHideTimeout = 100;

  var _isDragging = false;
  var _isDecelerating = false;
  var _isScrolling = false;
  var _startAccelerateX = 0;
  var _startAccelerateY = 0;
  var _lastMouseX = -1;
  var _lastMouseY = -1;
  var _lastTimeStamp = Date.now();
  var _decelerationAnimationInterval = null;

  var $window = $(window['addEventListener'] ? window : document.body);
  var $element = this.$element = $(element);
  element = this.element = $(element).get(0);

  var content = this.content = new SKScrollContent(this);
  var horizontalScrollBar = this.horizontalScrollBar = new SKScrollBar(this, SKScrollBarType.Horizontal);
  var verticalScrollBar = this.verticalScrollBar = new SKScrollBar(this, SKScrollBarType.Vertical);
  var self = this;

  var startDeceleration = function(startTime) {
    var acceleration = (startTime - _lastTimeStamp) / kAcceleration;
    var accelerateDeltaX = self.x - _startAccelerateX;
    var accelerateDeltaY = self.y - _startAccelerateY;
    var velocityX = accelerateDeltaX / acceleration;
    var velocityY = accelerateDeltaY / acceleration;
    var minimumX = self.minimumX;
    var minimumY = self.minimumY;
    var elasticDeceleration = kElasticDeceleration;
    var elasticAcceleration = kElasticAcceleration;
    var lastFrameTime = 0;

    var stepAnimation = function (currentFrameTime) {
      if (!_isDecelerating) return;

      var deltaTime = currentFrameTime - lastFrameTime;
      var x = self.x = self.x + (!self.canScrollHorizontal() ? 0 : velocityX);
      var y = self.y = self.y + (!self.canScrollVertical() ? 0 : velocityY);

      velocityX *= kDecelerationFactor;
      velocityY *= kDecelerationFactor;

      if (Math.abs(velocityX) <= kMinimumVelocity &&
          Math.abs(velocityY) <= kMinimumVelocity) {
        _isDecelerating = false;
        scrollEnd();
        return;
      }

      content.translate(x, y);
      _decelerationAnimationInterval = window.requestAnimationFrame(stepAnimation);

      var elasticX = (x < minimumX) ? minimumX - x : (x > 0) ? -x : 0;
      var elasticY = (y < minimumY) ? minimumY - y : (y > 0) ? -y : 0;

      if (elasticX) velocityX = (elasticX * velocityX <= 0) ?
        velocityX + (elasticX * elasticDeceleration) : elasticX * elasticAcceleration;

      if (elasticY) velocityY = (elasticY * velocityY <= 0) ?
        velocityY + (elasticY * elasticDeceleration) : elasticY * elasticAcceleration;

      lastFrameTime = currentFrameTime;
    };

    if (Math.abs(velocityX) > kMinimumDecelerationVelocity ||
        Math.abs(velocityY) > kMinimumDecelerationVelocity) {
      _isDecelerating = true;
      _decelerationAnimationInterval = window.requestAnimationFrame(stepAnimation);
      lastFrameTime = Date.now();
    } else {
      scrollEnd();
    }
  };

  var stopDeceleration = function() {
    _isDecelerating = false;
    window.cancelAnimationFrame(_decelerationAnimationInterval);
  };

  var scrollStart = function() {
    _isScrolling = true;
    $element.trigger(SKScrollEventType.ScrollStart);
  };

  var scrollEnd = function() {
    _isScrolling = false;

    horizontalScrollBar.hide();
    verticalScrollBar.hide();

    var x = Math.round(self.x);
    var y = Math.round(self.y);

    self.x = x;
    self.y = y;

    content.translate(x, y);

    $element.trigger(SKScrollEventType.ScrollEnd);
  };

  var bounceTransitionEndHandler = function(evt) {
    $element.unbind(evt);
    scrollEnd();
  };

  $element.bind('mousedown touchstart', function(evt) {
    var scrollViewSize = self.getSize();
    var contentSize = content.getSize();
    var x = _startAccelerateX = self.x;
    var y = _startAccelerateY = self.y;
    var minimumX = self.minimumX = scrollViewSize.width - contentSize.width;
    var minimumY = self.minimumY = scrollViewSize.height - contentSize.height;

    _isDragging = true;
    _lastMouseX = (evt.type === 'touchstart') ? evt.originalEvent.touches[0].pageX : evt.pageX;
    _lastMouseY = (evt.type === 'touchstart') ? evt.originalEvent.touches[0].pageY : evt.pageY;
    _lastTimeStamp = evt.timeStamp;

    stopDeceleration();

    if (self.canScrollHorizontal()) {
      horizontalScrollBar.show();
      horizontalScrollBar.update(true);
    }

    if (self.canScrollVertical()) {
      verticalScrollBar.show();
      verticalScrollBar.update(true);
    }

    evt.preventDefault();
  });

  $window.bind('mousemove touchmove', function(evt) {
    if (!_isDragging) return;

    var mouseX = (evt.type === 'touchmove') ? evt.originalEvent.touches[0].pageX : evt.pageX;
    var mouseY = (evt.type === 'touchmove') ? evt.originalEvent.touches[0].pageY : evt.pageY;
    var deltaX = mouseX - _lastMouseX;
    var deltaY = mouseY - _lastMouseY;
    var minimumX = self.minimumX;
    var minimumY = self.minimumY;
    var x = self.x + deltaX;
    var y = self.y + deltaY;

    x -= ((x < minimumX) ? deltaX : (x > 0) ? deltaX : 0) / 2;
    y -= ((y < minimumY) ? deltaY : (y > 0) ? deltaY : 0) / 2;

    self.x = !self.canScrollHorizontal() ? 0 : x;
    self.y = !self.canScrollVertical() ? 0 : y;

    content.translate(self.x, self.y);

    var accelerationTime = evt.timeStamp - _lastTimeStamp;

    if (accelerationTime > kAccelerationTimeout) {
      _startAccelerateX = self.x;
      _startAccelerateY = self.y;
      _lastTimeStamp = evt.timeStamp;
    }

    if (self.canScrollHorizontal()) horizontalScrollBar.update();
    if (self.canScrollVertical()) verticalScrollBar.update();
    if (!_isScrolling) scrollStart();

    _lastMouseX = mouseX;
    _lastMouseY = mouseY;
  });

  $window.bind('mouseup touchend', function(evt) {
    if (!_isDragging) return;

    var timeStamp = evt.timeStamp;
    var accelerationTime = timeStamp - _lastTimeStamp;
    var minimumX = self.minimumX;
    var minimumY = self.minimumY;
    var x = Math.min(Math.max(minimumX, self.x), 0);
    var y = Math.min(Math.max(minimumY, self.y), 0);

    var bounce = function() {
      $element.bind('webkitTransitionEnd transitionend MSTransitionEnd oTransitionEnd transitionEnd', bounceTransitionEndHandler);
      content.translate(x, y, kBounceTransitionDuration);
      self.x = _startAccelerateX = !self.canScrollHorizontal() ? 0 : x;
      self.y = _startAccelerateX = !self.canScrollVertical() ? 0 : y;
    };

    if (accelerationTime < kAccelerationTimeout) {
      if (x !== self.x || y !== self.y) {
        bounce();
      } else {
        startDeceleration(timeStamp);
      }
    } else if (x !== self.x || y !== self.y) {
      bounce();
    } else {
      scrollEnd();
    }

    _isDragging = false;
    _lastMouseX = -1;
    _lastMouseY = -1;
    _lastTimeStamp = timeStamp;
  });

  $element.data('scrollView', this);
};

SKScrollView.prototype = {
  minimumX: 0,
  minimumY: 0,
  element: null,
  $element: null,
  content: null,
  horizontalScrollBar: null,
  verticalScrollBar: null,
  x: 0,
  y: 0,
  canScrollHorizontal: function() { return (this.minimumX < 0); },
  canScrollVertical: function() { return (this.minimumY < 0); },
  getSize: function() {
    var $element = this.$element;
    return { width: $element.width(), height: $element.height() };
  }
};

var SKScrollContent = function(scrollView) {
  this.scrollView = scrollView;
  scrollView.$element.wrapInner('
');

  var $element = this.$element = scrollView.$element.children('.sk-scroll-content');
  var element = this.element = $element.get(0);
};

SKScrollContent.prototype = {
  element: null,
  $element: null,
  scrollView: null,
  getSize: function() {
    var $element = this.$element;
    return { width: $element.width(), height: $element.height() };
  },
  translate: function(x, y, duration) {
    var translate3d = 'translate3d(' + x + 'px, ' + y + 'px, 0)';
    var translate = 'translate(' + x + 'px, ' + y + 'px)';

    duration = (duration) ? duration + 's' : '0s';

    this.$element.css({
      '-webkit-transition-duration': duration,
      '-moz-transition-duration': duration,
      '-ms-transition-duration': duration,
      '-o-transition-duration': duration,
      'transition-duration': duration,
      '-webkit-transform': translate3d,
      '-moz-transform': translate,
      '-ms-transform': translate,
      '-o-transform': translate,
      'transform': translate
    });

    var scrollView = this.scrollView;

    if (scrollView.canScrollHorizontal()) scrollView.horizontalScrollBar.update();
    if (scrollView.canScrollVertical()) scrollView.verticalScrollBar.update();
  }
};

var SKScrollBar = function(scrollView, type) {
  this.scrollView = scrollView;
  this.type = type;

  var $element = this.$element = $('
');
  var element = this.element = $element.get(0);

  scrollView.$element.append($element);

  if (this.type === SKScrollBarType.Horizontal) {
    this.width = parseInt($element.css('height'), 10);
    $element.css({ 'bottom': '0', 'left': '0' });
  } else {
    this.width = parseInt($element.css('width'), 10);
    $element.css({ 'top': '0', 'right': '0' });
  }
};

SKScrollBar.prototype = {
  _size: 0,
  _scrollViewSize: null,
  _contentSize: null,
  element: null,
  $element: null,
  scrollView: null,
  type: SKScrollBarType.Horizontal,
  minimumSize: 34,
  width: 5,
  setSize: function(value) {
    this._size = value;
    this.$element.css((this.type === SKScrollBarType.Horizontal) ? 'width' : 'height', value + 'px');
  },
  update: function(recalculateSize) {
    var scrollView = this.scrollView;
    var minimumSize = this.minimumSize;
    var width = this.width;
    var type = this.type;
    var content = scrollView.content;
    var scrollViewSize, contentSize

    if (recalculateSize) {
      scrollViewSize = this._scrollViewSize = scrollView.getSize();
      contentSize = this._contentSize = content.getSize();
    } else {
      scrollViewSize = this._scrollViewSize;
      contentSize = this._contentSize;
    }

    var canScrollHorizontal = scrollView.canScrollHorizontal();
    var canScrollVertical = scrollView.canScrollVertical();
    var margin, size, scrollPosition, minimumPosition, position, translate3d, translate;

    if (type === SKScrollBarType.Horizontal) {
      margin = (canScrollVertical ? width * 2 : width) + 1;
      scrollPosition = scrollView.x;
      size = (!recalculateSize && minimumPosition < scrollPosition && scrollPosition < 0) ?
        this._size : Math.max(minimumSize, Math.round(
          (scrollViewSize.width / contentSize.width) * (scrollViewSize.width - margin)
        ));

      minimumPosition = scrollView.minimumX;
      position = (scrollPosition / minimumPosition) * (scrollViewSize.width - margin - size);

      if (scrollPosition > 0) {
        size = Math.round(Math.max(size - scrollPosition, width));
        position = 1;
      } else if (scrollPosition < minimumPosition) {
        size = Math.round(Math.max(size - minimumPosition + scrollPosition, width));
        position = scrollViewSize.width - size - margin;
      }

      translate3d = 'translate3d(' + position + 'px, 0, 0)';
      translate = 'translate(' + position + 'px, 0)';
    } else {
      margin = (canScrollHorizontal ? width * 2 : width) + 1;
      scrollPosition = scrollView.y;
      size = (!recalculateSize && minimumPosition < scrollPosition && scrollPosition < 0) ?
        this._size : Math.max(minimumSize, Math.round(
          (scrollViewSize.height / contentSize.height) * (scrollViewSize.height - margin)
        ));

      minimumPosition = scrollView.minimumY;
      position = (scrollPosition / minimumPosition) * (scrollViewSize.height - margin - size);

      if (scrollPosition > 0) {
        size = Math.round(Math.max(size - scrollPosition, width));
        position = 1;
      } else if (scrollPosition < minimumPosition) {
        size = Math.round(Math.max(size - minimumPosition + scrollPosition, width));
        position = scrollViewSize.height - size - margin;
      }

      translate3d = 'translate3d(0, ' + position + 'px, 0)';
      translate = 'translate(0, ' + position + 'px)';
    }

    if (this._size !== size) this.setSize(size);

    this.$element.css({
      '-webkit-transform': translate3d,
      '-moz-transform': translate,
      '-ms-transform': translate,
      '-o-transform': translate,
      'transform': translate
    });
  },
  show: function() {
    this.$element.addClass('active');
  },
  hide: function() {
    this.$element.removeClass('active');
  }
};

$(function() {
  $('.sk-scroll-view').each(function(index, element) { new SKScrollView(element); });
});
<|endoftext|>