Adding mapbox-gl branch

This commit is contained in:
Andreas Hocevar
2015-03-16 18:50:27 +01:00
parent 7985f030fa
commit 57ee7f52fd
3109 changed files with 943365 additions and 0 deletions
@@ -0,0 +1,288 @@
// Copyright 2014 The Closure Library Authors. All Rights Reserved.
//
// 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.
/**
* @fileoverview goog.dom.animationFrame permits work to be done in-sync with
* the render refresh rate of the browser and to divide work up globally based
* on whether the intent is to measure or to mutate the DOM. The latter avoids
* repeated style recalculation which can be really slow.
*
* Goals of the API:
* <ul>
* <li>Make it easy to schedule work for the next animation frame.
* <li>Make it easy to only do work once per animation frame, even if two
* events fire that trigger the same work.
* <li>Make it easy to do all work in two phases to avoid repeated style
* recalculation caused by interleaved reads and writes.
* <li>Avoid creating closures per schedule operation.
* </ul>
*
*
* Programmatic:
* <pre>
* var animationTask = goog.dom.animationFrame.createTask({
* measure: function(state) {
* state.width = goog.style.getSize(elem).width;
* this.animationTask();
* },
* mutate: function(state) {
* goog.style.setWidth(elem, Math.floor(state.width / 2));
* }
* }, this);
* });
* </pre>
*
* See also
* https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame
*/
goog.provide('goog.dom.animationFrame');
goog.provide('goog.dom.animationFrame.Spec');
goog.provide('goog.dom.animationFrame.State');
goog.require('goog.dom.animationFrame.polyfill');
// Install the polyfill.
goog.dom.animationFrame.polyfill.install();
/**
* @typedef {{
* id: number,
* fn: !Function,
* context: (!Object|undefined)
* }}
* @private
*/
goog.dom.animationFrame.Task_;
/**
* @typedef {{
* measureTask: goog.dom.animationFrame.Task_,
* mutateTask: goog.dom.animationFrame.Task_,
* state: (!Object|undefined),
* args: (!Array|undefined),
* isScheduled: boolean
* }}
* @private
*/
goog.dom.animationFrame.TaskSet_;
/**
* @typedef {{
* measure: (!Function|undefined),
* mutate: (!Function|undefined)
* }}
*/
goog.dom.animationFrame.Spec;
/**
* A type to represent state. Users may add properties as desired.
* @constructor
* @final
*/
goog.dom.animationFrame.State = function() {};
/**
* Saves a set of tasks to be executed in the next requestAnimationFrame phase.
* This list is initialized once before any event firing occurs. It is not
* affected by the fired events or the requestAnimationFrame processing (unless
* a new event is created during the processing).
* @private {!Array<!Array<goog.dom.animationFrame.TaskSet_>>}
*/
goog.dom.animationFrame.tasks_ = [[], []];
/**
* Values are 0 or 1, for whether the first or second array should be used to
* lookup or add tasks.
* @private {number}
*/
goog.dom.animationFrame.doubleBufferIndex_ = 0;
/**
* Whether we have already requested an animation frame that hasn't happened
* yet.
* @private {boolean}
*/
goog.dom.animationFrame.requestedFrame_ = false;
/**
* Counter to generate IDs for tasks.
* @private {number}
*/
goog.dom.animationFrame.taskId_ = 0;
/**
* Whether the animationframe runTasks_ loop is currently running.
* @private {boolean}
*/
goog.dom.animationFrame.running_ = false;
/**
* Returns a function that schedules the two passed-in functions to be run upon
* the next animation frame. Calling the function again during the same
* animation frame does nothing.
*
* The function under the "measure" key will run first and together with all
* other functions scheduled under this key and the function under "mutate" will
* run after that.
*
* @param {{
* measure: (function(this:THIS, !goog.dom.animationFrame.State)|undefined),
* mutate: (function(this:THIS, !goog.dom.animationFrame.State)|undefined)
* }} spec
* @param {THIS=} opt_context Context in which to run the function.
* @return {function(...?)}
* @template THIS
*/
goog.dom.animationFrame.createTask = function(spec, opt_context) {
var genericSpec = /** @type {!goog.dom.animationFrame.Spec} */ (spec);
var id = goog.dom.animationFrame.taskId_++;
var measureTask = {
id: id,
fn: spec.measure,
context: opt_context
};
var mutateTask = {
id: id,
fn: spec.mutate,
context: opt_context
};
var taskSet = {
measureTask: measureTask,
mutateTask: mutateTask,
state: {},
args: undefined,
isScheduled: false
};
return function() {
// Default the context to the one that was used to call the tasks scheduler
// (this function).
if (!opt_context) {
measureTask.context = this;
mutateTask.context = this;
}
// Save args and state.
if (arguments.length > 0) {
// The state argument goes last. That is kinda horrible but compatible
// with {@see wiz.async.method}.
if (!taskSet.args) {
taskSet.args = [];
}
taskSet.args.length = 0;
taskSet.args.push.apply(taskSet.args, arguments);
taskSet.args.push(taskSet.state);
} else {
if (!taskSet.args || taskSet.args.length == 0) {
taskSet.args = [taskSet.state];
} else {
taskSet.args[0] = taskSet.state;
taskSet.args.length = 1;
}
}
if (!taskSet.isScheduled) {
taskSet.isScheduled = true;
var tasksArray = goog.dom.animationFrame.tasks_[
goog.dom.animationFrame.doubleBufferIndex_];
tasksArray.push(taskSet);
}
goog.dom.animationFrame.requestAnimationFrame_();
};
};
/**
* Run scheduled tasks.
* @private
*/
goog.dom.animationFrame.runTasks_ = function() {
goog.dom.animationFrame.running_ = true;
goog.dom.animationFrame.requestedFrame_ = false;
var tasksArray = goog.dom.animationFrame
.tasks_[goog.dom.animationFrame.doubleBufferIndex_];
var taskLength = tasksArray.length;
// During the runTasks_, if there is a recursive call to queue up more
// task(s) for the next frame, we use double-buffering for that.
goog.dom.animationFrame.doubleBufferIndex_ =
(goog.dom.animationFrame.doubleBufferIndex_ + 1) % 2;
var task;
// Run all the measure tasks first.
for (var i = 0; i < taskLength; ++i) {
task = tasksArray[i];
var measureTask = task.measureTask;
task.isScheduled = false;
if (measureTask.fn) {
// TODO (perumaal): Handle any exceptions thrown by the lambda.
measureTask.fn.apply(measureTask.context, task.args);
}
}
// Run the mutate tasks next.
for (var i = 0; i < taskLength; ++i) {
task = tasksArray[i];
var mutateTask = task.mutateTask;
task.isScheduled = false;
if (mutateTask.fn) {
// TODO (perumaal): Handle any exceptions thrown by the lambda.
mutateTask.fn.apply(mutateTask.context, task.args);
}
// Clear state for next vsync.
task.state = {};
}
// Clear the tasks array as we have finished processing all the tasks.
tasksArray.length = 0;
goog.dom.animationFrame.running_ = false;
};
/**
* @return {boolean} Whether the animationframe is currently running. For use
* by callers who need not to delay tasks scheduled during runTasks_ for an
* additional frame.
*/
goog.dom.animationFrame.isRunning = function() {
return goog.dom.animationFrame.running_;
};
/**
* Request {@see goog.dom.animationFrame.runTasks_} to be called upon the
* next animation frame if we haven't done so already.
* @private
*/
goog.dom.animationFrame.requestAnimationFrame_ = function() {
if (goog.dom.animationFrame.requestedFrame_) {
return;
}
goog.dom.animationFrame.requestedFrame_ = true;
window.requestAnimationFrame(goog.dom.animationFrame.runTasks_);
};
@@ -0,0 +1,265 @@
// Copyright 2014 The Closure Library Authors. All Rights Reserved.
//
// 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.
/**
* @fileoverview Tests for goog.dom.animationFrame.
*/
goog.setTestOnly();
goog.require('goog.dom.animationFrame');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.jsunit');
var NEXT_FRAME = goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT;
var mockClock;
var t0, t1;
var result;
function setUp() {
mockClock = new goog.testing.MockClock(true);
result = '';
t0 = goog.dom.animationFrame.createTask({
measure: function() {
result += 'me0';
},
mutate: function() {
result += 'mu0';
}
});
t1 = goog.dom.animationFrame.createTask({
measure: function() {
result += 'me1';
},
mutate: function() {
result += 'mu1';
}
});
assertEquals('', result);
}
function tearDown() {
mockClock.dispose();
}
function testCreateTask_one() {
t0();
assertEquals('', result);
mockClock.tick(NEXT_FRAME);
assertEquals('me0mu0', result);
mockClock.tick(NEXT_FRAME);
assertEquals('me0mu0', result);
t0();
t0(); // Should do nothing.
mockClock.tick(NEXT_FRAME);
assertEquals('me0mu0me0mu0', result);
}
function testCreateTask_onlyMutate() {
t0 = goog.dom.animationFrame.createTask({
mutate: function() {
result += 'mu0';
}
});
t0();
assertEquals('', result);
mockClock.tick(NEXT_FRAME);
assertEquals('mu0', result);
}
function testCreateTask_onlyMeasure() {
t0 = goog.dom.animationFrame.createTask({
mutate: function() {
result += 'me0';
}
});
t0();
assertEquals('', result);
mockClock.tick(NEXT_FRAME);
assertEquals('me0', result);
}
function testCreateTask_two() {
t0();
t1();
assertEquals('', result);
mockClock.tick(NEXT_FRAME);
assertEquals('me0me1mu0mu1', result);
mockClock.tick(NEXT_FRAME);
assertEquals('me0me1mu0mu1', result);
t0();
t1();
t0();
t1();
mockClock.tick(NEXT_FRAME);
assertEquals('me0me1mu0mu1me0me1mu0mu1', result);
}
function testCreateTask_recurse() {
var stop = false;
var recurse = goog.dom.animationFrame.createTask({
measure: function() {
if (!stop) {
recurse();
}
result += 're0';
},
mutate: function() {
result += 'ru0';
}
});
recurse();
mockClock.tick(NEXT_FRAME);
assertEquals('re0ru0', result);
mockClock.tick(NEXT_FRAME);
assertEquals('re0ru0re0ru0', result);
mockClock.tick(NEXT_FRAME);
assertEquals('re0ru0re0ru0re0ru0', result);
t0();
stop = true;
mockClock.tick(NEXT_FRAME);
assertEquals('re0ru0re0ru0re0ru0re0me0ru0mu0', result);
// Recursion should have stopped now.
mockClock.tick(NEXT_FRAME);
assertEquals('re0ru0re0ru0re0ru0re0me0ru0mu0', result);
assertFalse(goog.dom.animationFrame.requestedFrame_);
mockClock.tick(NEXT_FRAME);
assertEquals('re0ru0re0ru0re0ru0re0me0ru0mu0', result);
assertFalse(goog.dom.animationFrame.requestedFrame_);
}
function testCreateTask_recurseTwoMethodsWithState() {
var stop = false;
var recurse1 = goog.dom.animationFrame.createTask({
measure: function(state) {
if (!stop) {
recurse2();
}
result += 'r1e0';
state.text = 'T0';
},
mutate: function(state) {
result += 'r1u0' + state.text;
}
});
var recurse2 = goog.dom.animationFrame.createTask({
measure: function(state) {
if (!stop) {
recurse1();
}
result += 'r2e0';
state.text = 'T1';
},
mutate: function(state) {
result += 'r2u0' + state.text;
}
});
var taskLength = goog.dom.animationFrame.tasks_[0].length;
recurse1();
mockClock.tick(NEXT_FRAME);
// Only recurse1 executed.
assertEquals('r1e0r1u0T0', result);
mockClock.tick(NEXT_FRAME);
// Recurse2 executed and queueup recurse1.
assertEquals('r1e0r1u0T0r2e0r2u0T1', result);
mockClock.tick(NEXT_FRAME);
// Recurse1 executed and queueup recurse2.
assertEquals('r1e0r1u0T0r2e0r2u0T1r1e0r1u0T0', result);
stop = true;
mockClock.tick(NEXT_FRAME);
// Recurse2 executed and should have stopped.
assertEquals('r1e0r1u0T0r2e0r2u0T1r1e0r1u0T0r2e0r2u0T1', result);
assertFalse(goog.dom.animationFrame.requestedFrame_);
mockClock.tick(NEXT_FRAME);
assertEquals('r1e0r1u0T0r2e0r2u0T1r1e0r1u0T0r2e0r2u0T1', result);
assertFalse(goog.dom.animationFrame.requestedFrame_);
mockClock.tick(NEXT_FRAME);
assertEquals('r1e0r1u0T0r2e0r2u0T1r1e0r1u0T0r2e0r2u0T1', result);
assertFalse(goog.dom.animationFrame.requestedFrame_);
}
function testCreateTask_args() {
var context = {context: true};
var s = goog.dom.animationFrame.createTask({
measure: function(state) {
assertEquals(context, this);
assertUndefined(state.foo);
state.foo = 'foo';
},
mutate: function(state) {
assertEquals(context, this);
result += state.foo;
}
}, context);
s();
mockClock.tick(NEXT_FRAME);
assertEquals('foo', result);
var dynamicContext = goog.dom.animationFrame.createTask({
measure: function(state) {
assertEquals(context, this);
},
mutate: function(state) {
assertEquals(context, this);
result += 'bar';
}
});
dynamicContext.call(context);
mockClock.tick(NEXT_FRAME);
assertEquals('foobar', result);
var moreArgs = goog.dom.animationFrame.createTask({
measure: function(event, state) {
assertEquals(context, this);
assertEquals('event', event);
state.baz = 'baz';
},
mutate: function(event, state) {
assertEquals('event', event);
assertEquals(context, this);
result += state.baz;
}
});
moreArgs.call(context, 'event');
mockClock.tick(NEXT_FRAME);
assertEquals('foobarbaz', result);
}
function testIsRunning() {
var result = '';
var task = goog.dom.animationFrame.createTask({
measure: function() {
result += 'me';
assertTrue(goog.dom.animationFrame.isRunning());
},
mutate: function() {
result += 'mu';
assertTrue(goog.dom.animationFrame.isRunning());
}
});
task();
assertFalse(goog.dom.animationFrame.isRunning());
mockClock.tick(NEXT_FRAME);
assertFalse(goog.dom.animationFrame.isRunning());
assertEquals('memu', result);
}
@@ -0,0 +1,61 @@
// Copyright 2014 The Closure Library Authors. All Rights Reserved.
//
// 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.
/**
* @fileoverview A polyfill for window.requestAnimationFrame and
* window.cancelAnimationFrame.
* Code based on https://gist.github.com/paulirish/1579671
*/
goog.provide('goog.dom.animationFrame.polyfill');
/**
* @define {boolean} If true, will install the requestAnimationFrame polyfill.
*/
goog.define('goog.dom.animationFrame.polyfill.ENABLED', true);
/**
* Installs the requestAnimationFrame (and cancelAnimationFrame) polyfill.
*/
goog.dom.animationFrame.polyfill.install =
goog.dom.animationFrame.polyfill.ENABLED ? function() {
var vendors = ['ms', 'moz', 'webkit', 'o'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] +
'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] +
'CancelAnimationFrame'] ||
window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
var lastTime = 0;
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
lastTime = currTime + timeToCall;
return window.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
};
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
}
} : goog.nullFunction;