commit bacfdb514b5873cba36ca75a169918055405f559 Author: The Webmaker Team Date: Fri May 10 15:41:57 2013 -0400 Compress initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..19f9af1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +*.swp +*~ +dist +.DS_Store +*.bak +*.webm +*.mov +*.avi +*.ogg +*.ogv +*.oga +*.mp4 +*.wav +*.mp3 +*.sqlite +node_modules +cornfield/view +css/butter.ui.css +css/transitions.css +css/embed-shell.css +cornfield/config/runtime.json +cornfield/config/versions.json +docs +css/embed.css +templates/assets/plugins/wikipedia/popcorn.wikipedia.css +templates/assets/css/jquery-ui/jquery.ui.butter.css +src/ui/webmakernav/webmakernav.css diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..b30620d4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "external/popcorn-js"] + path = external/popcorn-js + url = git://github.com/mozilla/popcorn-js.git +[submodule "external/html5-lint"] + path = external/html5-lint + url = git://github.com/mozilla/html5-lint.git diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..f4392691 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,65 @@ +{ + // Environments + "browser" : true, + "devel": true, + "node" : true, + "nonstandard": true, + "predef": [ + "Popcorn", + "requirejs", + "require", + "define" + ], + + // Enforcing + //"bitwise": true, + //"camelcase": true, + "curly": true, + "eqeqeq": true, + "forin": true, + "immed": true, + //"indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "noempty": true, + "nonew": true, + //"plusplus": true, + //"quotmark": true, + //"regexp": true, + "undef": true, + "unused": true, + "strict": false, + "trailing": true, + //"maxparams": 4, + //"maxdepth": 3, + //"maxstatements": 4, + //"maxcomplexity": 5, + + // Relaxing + //"asi": true, + //"boss": true, + //"debug": true, + //"eqnull": true, + "es5" : true, + //"esnext": true, + //"evil": true, + //"expr": true, + //"funcscope": true, + "globalstrict": true + //"iterator": true, + //"lastsemic": true, + //"laxbreak": true, + //"laxcomma": true, + //"loopfunc": true, + //"multistr": true, + //"onecase": true, + //"proto": true, + //"regexdash": true, + //"scripturl": true, + //"smarttabs": true, + //"shadow": true, + //"sub": true + //"supernew": true, + //"validthis": true, +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..344e26e1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: node_js +node_js: + - 0.8 +before_script: node make check +notifications: + irc: + channels: + - "irc.mozilla.org#popcorn" + on_success: change diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..8618cfa4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2011, 2012 Mozilla Foundation + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENSE_HEADER b/LICENSE_HEADER new file mode 100644 index 00000000..c9c679ce --- /dev/null +++ b/LICENSE_HEADER @@ -0,0 +1,3 @@ +/*! This Source Code Form is subject to the terms of the MIT license + * If a copy of the MIT license was not distributed with this file, you can + * obtain one at http://www.mozillapopcorn.org/butter-license.txt */ diff --git a/README.md b/README.md new file mode 100644 index 00000000..fb1749d0 --- /dev/null +++ b/README.md @@ -0,0 +1,278 @@ +Butter +====== + +An SDK for authoring Popcorn projects. + +Supported Platforms +------------------- + +We're writing Butter so that it runs in modern, HTML5 compatible browsers. For version 1.0, we're targeting modern HTML5 desktop browsers. In the current version we are supporting: + +### Desktop: +* Chrome stable +* Firefox stable +* Internet Explorer 9+ +* Safari stable + +Prerequisites +------------- + +* node v0.8 or higher +* npm (comes with node v0.8 installer) +* mongodb v2.0.8 or higher +* A working build environment: + * Mac OS X - Xcode or Command Line Tools package + * Windows - Python 2.5+ and Visual Studio 2010 (C++ Express edition works fine) + * Linux - build-essential package on Debian/Ubuntu, or the equivalent for your distro + +Environment Setup +----------------- + +1. `git clone --recursive https://github.com/mozilla/butter.git` +2. `cd butter` +3. `npm install` + +Running Butter in development mode +---------------------------------- + +1. Run `node make server`. +2. Navigate to [http://localhost:8888/](http://localhost:8888/) in your favourite browser. + +If you want to change the bind IP or port check the Configuration section below. + +Packaging and Distributing Butter +-------------------------------- + +Running `node make deploy` will compile all the necessary files into the `dist/` folder. +Run `NODE_ENV=production node app.js` in the `dist/cornfield` directory in order to run the server in production mode. + +Cornfield +--------- + +Cornfield is PopcornMaker's back-end server system, designed to serve content to users, store their ongoing work, and publish what they've done. + +### Storage + +There are two types of storage Cornfield needs to run: + +* A database: To store user project data, a database is required. Cornfield uses the `Sequelize` middleware, so multiple database options are available. You are responsible for setting up and maintaining a PostgreSQL or MySQL database enrivonment if you choose to use either, so make sure a user has access to a database called `popcorn` for cornfield to run correctly. + +* A data-blob store: To store published projects, Cornfield can use the filesystem, or Amazon's S3. See the configuration options below for setting up this feature for your environment. + + +### Configuration + +There are several configuration files in cornfield/config/ that control how cornfield works. +They are applied in order from most general to most specific to present one configuration +to the server: + +1. default.json +2. _hostname_.json +3. _environment_.json +4. _hostname_-_environment_.json +5. runtime.json + +_hostname_ and _environment_ are variable: + +* _hostname_ - The hostname of the machine. Defaults to the output of `hostname` on the cli. +* _environment_ - The value of the `NODE_ENV` environment variable. Defaults to `development`. + +To change the cornfield configuration for your deployment of Butter, it's best to create a +new file called _hostname_-_environment_.json that overrides the cornfield defaults. + +#### Configuration Options + + - `server` settings for the cornfield server + - `bindIP` the IP or hostname to use for the server (e.g., localhost). + - `bindPort` the Port number to use for the server (e.g., 8888). If using a port number lower than 1024, the server will have to be run as root. + - `logger` settings for server logging + - `format` the logging format to use. Possible values include: default, short, tiny, dev. + - `session` settings for user sessions + - `secret` the sessions secret (i.e., some long string) + - `duration` the session's duration (e.g., 2419200000) + - `staticMiddleware` settings for cornfield Connect middleware + - `maxAge` the max age of static assests + - `dirs` settings for various directories, paths, hostnames + - `wwwRoot` the server's WWW root directory (e.g., `../`) + - `templates` the location of templates (e.g., `../templates`) + - `appHostname` the hostname URL for the application, usually the same as `server.bindIP` and `server.bindPort` (e.g., `http://localhost:8888`) + - `embedHostname` [optional] the hostname URL where published embed documents are stored, if different from `dirs.appHostname` (e.g., `http://s3.amazonaws.com/your-bucket`) + - `templates` list of templates to serve. The format is as follows: + ``: `{{templateBase}}`. The `{{templateBase}}` string will be replaced by the value in `dirs.templates` (e.g., "basic": "{{templateBase}}basic/config.json") + + - `exportedAssets` list of scripts to include in exported assets. These are things like popcorn.js or other scripts that your exported projects depend upon in order to run. + + - `additionalStaticRoots` list of additional roots to use. + + - `database` database configuration options + - `database` the database name. Used by mysql and postgresql + - `username` the username to use when connecting to the database. Used by mysql and postgresql + - `password` the password for the username. Used by mysql and postgresql + - `options` additional sequelize options. Please see the [sequelize manual](http://www.sequelizejs.com/#usage-options) for the complete listing. + - `dialect` the sql dialect of the database. Default is `mysql`, must be one of `mysql`, `sqlite`, or `postgresql` + - `storage` the storage engine for sqlite. Default is `:memory:`, an in-memory db, must be a string representing a file path or `:memory:` + - `logging` function to print sql queries to console. Default is `console.log`, must be a function or `false` + - `host` hostname of the mysql or postgresql server. Default is `localhost` + - `port` port of the mysql or postgresql server. Default is `3306` + - `pool` connection pooling options for mysql and postgresql. Default is none + - `maxConnections` - maximum number of connections open in the pool + - `maxIdleTime` - maximum time in seconds to leave an idle connection open in the pool + + - `publishStore` a `fileStore` used to publish project HTML files (see `fileStore` below for details) + + - `feedbackStore` a `fileStore` used to publish feedback from the user as JSON (see `fileStore` below for details) + + - `crashStore` a `fileStore` used to publish crash reports from the user as JSON (see `fileStore` below for details) + +The `fileStore` type is used to setup a backend for storing data: + + - `type` the type of file store to use. Possible values include `local` (i.e., local file system) and `s3` (i.e., Amazon S3) + - `options` options for the file store, which depends on the type chosen. + - local options + - `root` the root directory under which all exported files are placed (e.g., `./view`) + - `namePrefix` [optional] the path prefix to add to any filenames passed to the local file store. For example, if using "v" all filenames will become "v/" + - `nameSuffix` [optional] the filename suffix to use for all filenames (e.g., ".html") + - s3 options + - `key` the AWS S3 key to use for authentication + - `secret` the AWS S3 secret to use for authentication + - `bucket` the AWS S3 bucket name to use for storing key/value pairs + - `namePrefix` [optional] the prefix to add to any key names passed to the s3 file store. For example, if using "v" all keys will become "v/" + - `nameSuffix` [optional] the suffix to add to any key names passed to the s3 file store. For example, if using ".json" all keys will end in ".json" + - `contentType` [optional] the mime type to use for data written to S3. If none given `text/plain` is used. + +### Sample production config + +`alice-production.json:` + +This sample config uses a mix of the local file system as well as Amazon S3 for storage. + +```javascript +{ + "server" : { + "bindIP" : "0.0.0.0", + "bindPort" : "80" + }, + "logger" : { + "format" : "default" + }, + "session" : { + "secret": "1721f7a15316469fa4a9-5117d0d20e9f" + }, + "staticMiddleware": { + "maxAge": "3600000" + }, + "dirs": { + "appHostname": "http://example.org", + "embedHostname": "http://s3.amazonaws.com/my-bucket" + }, + "publishStore": { + "type": "s3", + "options": { + "namePrefix": "v", + "key": "my-s3-key", + "secret": "my-s3-secret", + "contentType": "text/html" + } + }, + "feedbackStore": { + "type": "local", + "options": { + "root": "./view", + "namePrefix": "feedback", + "nameSuffix": ".json" + } + }, + "crashStore": { + "type": "local", + "options": { + "root": "./view", + "namePrefix": "crash", + "nameSuffix": ".json" + } + } +} +``` + +Testing +------- + +Before contributing a new patch be sure to run the following: + +* Run `node make check` to lint butter +* Run `node make server` and navigate to `http://localhost:8888/test` to run the browser tests + +If you are contributing changes to cornfield, make sure you run the cornfield tests from the butter root directory this command: + +``` +npm test +``` + +Cornfield will attempt to run tests for using a mysql backend, but they will not run if mysql is not set up properly. To ensure that they run, use the `DB_HOST`, `DB_DATABASE`, `DB_USERNAME`, and `DB_PASSWORD` environment variables to tell the tests which host, database, username, and password to use respectively. If `DB_HOST` is left blank, `localhost` is assumed. + +Getting Involved +---------------- + +* Chat with the Popcorn community on irc.mozilla.org in the [#popcorn](irc://irc.mozilla.org/popcorn) channel. The developers hang out here on a daily basis. +* We also have a [mailing list](https://mail.mozilla.org/listinfo/community-popcorn) that you can subscribe to. +* File bugs and feature requests on our [issue tracker](https://webmademovies.lighthouseapp.com/projects/65733-butter/). +* The latest code can be found on our [Github repository](https://github.com/mozilla/butter/). +* If you'd like to contribute code, file a ticket on our issue tracker, and link to it from your Github pull request. + +Contributing Design +------------------- + +### Where to find/drop files + +Our design files are organized on dropbox, at this link: +https://www.dropbox.com/sh/7vm2rvw3axvkp0k/Tk4MKH4nZe +You can ask Kate ( k88hudson on IRC or Twitter ) to be added as a collaborator if you want to drop your files in here. + +### File System +* References: screen shots and other reference work from the webmaker project or HTML5 ecosystem +* Wireframes: documents about the functionality/interaction/description of features +* Visual Comps: UI mock-ups and style guide + +### Working with lighthouse +When someone assigns you a ticket, it will show up in your lighthouse queue with a status of `ui-comps-requested` or `assigned`. + +If you want someone to review or give feedback on your work, the best thing to do is: +* Put a link to your files in the ticket ( on our dropbox or somewhere externally ) +* Change the status of the ticket to `peer-review-requested` or `feedback-requested`, and choose a member of the team to be responsible. + +Contributing Code +----------------- + +### Working with Lighthouse +All of our code changes to Butter are documented in tickets, and go through two levels of peer-review. If you are interested in working on a ticket chose one from the [list of open tickets](https://webmademovies.lighthouseapp.com/projects/65733-popcorn-maker/milestones/current), ping any of the developers on IRC, and they will assign it to you. + +### Working with branches and making a pull-request + +* Each ticket is a separate branch, usually named after the ticket number. IE `t1234` +* All commit messages must include the ticket number. IE `Bug 1234 - Fixed a thing` or `[t1234] Fixed a thing` +* Before submitting a pull request, make sure you have rebased against the latest revision of master. + +### Getting review +All code changes in butter have to go through two levels of peer review. This improves the integrity of our code, and everyone goes through the process, from casual contributors to our most senior developers. + +1. After you have made a pull-request, post the link (e.g. https://github.com/mozilla/butter/pull/662) in the corresponding ticket. +2. Set the status of the ticket to `peer-review requested`, and choose someone to review your code. If you're not sure who should review your code, ask in #popcorn on IRC. +3. After you get a review, you will see (1) comments in the diff in your pull-request and (2) comments in lighthouse. Keep an eye on the ticket to see when your review is done. +4. Complete the changes that were requested, or if you disagree or need more information, comment in the pull-request or lighthouse. Commit and push up. +5. After your review passes, your reviewer will pass the ticket on to `super-review-requested`. You will likely have more changes after that review. +6. After your final round of changes, your ticket will change to `review-looks-good`. Hurrah! Ask someone to help you merge your code into master. + +### Landing (for those with commit rights to github.com/mozilla/butter) +Once code has been reviewed (PR+ and SR+), and you want to land it, you need to follow our rebase strategy. Don't use Github's "Merge pull request". Instead, please do the following: + +1. `git checkout master` +2. `git pull mozilla master` This assumes your remote to Mozilla's butter is called mozilla +3. `git checkout mybranch` +4. `git rebase -i master` Squash your commits here, where it makes sense +5. Make sure everything still works in Butter at this point +6. `git push origin mybranch --force` +7. `git checkout master` +8. `git merge mybranch --ff-only` No need for a commit message, as we're not doing a merge commit +9. `git push mozilla master` +10. Paste URL to commits in ticket, and mark it as "Staged" + +We use this landing strategy in order to make backing-out failed commits easier. diff --git a/changelog b/changelog new file mode 100644 index 00000000..f4a15fd8 --- /dev/null +++ b/changelog @@ -0,0 +1,253 @@ +0.3 Release, "Breakfast Club" - April 10, 2012 + +609 Add configuration that includes all plugins +604 [Meta] Install 0.3 on mozillapopcorn.org/edge +634 Need a way to set options on Popcorn +639 Create music video template +653 Saving doesn't work twice. +652 Simple URL replacement in Cornfield +650 Fix publish path +648 Dialogs need relative paths +647 Fix media element loading edgecase +646 Give editors own comm lib +643 Make cornfield's root file-serve path configurable +637 Fix positioning of butter-media-highlight +622 cornfield server should run by default, not simple node server +641 Editor module should auto-add editors from config +642 Update Popcorn submodule +596 replace Layla as default video +631 In-editor update of trackevent does not update the corresponding view +621 Server needs mongoose to run +626 Firefox textbox elements don't have proper height in dialogs +557 Fix import in test/selenium/testcases_tracks.py +616 Fix UI/Asset directory structure +598 Ability to turn Logger off +431 Common CSS for Dialogs +615 Remove cornfield/publish.js from repo +601 Test template exclude browser ID on publish +610 Fix inline-block inconsistency with Google Chrome on header buttons +611 Fix final lint errors +597 Published projects don't work +594 Bring cornfield tests back up to par +603 Turn off Media UI when butter.ui is disabled +600 src/core/trackevents.js is accessing butter when it doesn't exist +602 Make export button yellow like the others +320 Ability to import/export popcorn code per track +605 Export buttons aren't enabled +330 Implement publish project UI +466 Save indicator +563 Remove popcorn_outer.png +536 UI for specifying/changing timeline media +534 Name your project +535 Implement load project uI +537 Make Popcorn Maker header functional +585 Header behavior tweaks +584 After importing a project, trackevents aren't clickable. +402 Plugin tray UI needs to be brought up to par +529 Scrubber position resets to 0 if you attempt to drag it from the far right +458 Protect from invalid times in trackevents +436 have butter.io.cornfield write out a script tag to load BrowserID if it's not loaded +358 Add tooltip to unreadable trackevent title +579 Events are added 1 frame too early +569 PopcornWrapper breaks tests +583 Fix readme to indicate that --recursive clone is needed +364 Change HTML Generation to use Double quotes rather than single +242 generatePopcornString needs to pass options to Popcorn object +543 "When the tray shows anything other than the timeline, ""Add Popcorn"" should be ""Done"" +581 have we removed the ability to delete a track? +365 HTML Generation Moves Video Containing Element to Different Spot on Page +544 Add Track button +510 Remove reference to secretrobotron's github account +462 Phantom tracks when moving an event over a droppable target +276 in/out should indicate what they are measuring +539 Vertical scroll in timeline area with mousewheel +524 Refactor Media/Track/TrackEvent structure to be more light-weight +457 Dynamically load popcorn players on-demand +252 Don't let a user commit a blank field for timeline media change +494 Pasting video URL should not fail when no HTTP +437 Port all keyboard shortcuts from Popcorn Maker .1 into Butter +467 Elegant load for tray +482 clearProject should probably remove all tracks + trackEvents as well +495 At the end of the timeline, play should restart video +525 "Amalgamate ""cancel"" and ""ok"" buttons in default editor" +530 DragNDrop on Media elements is broken +460 "Create and implement UI for ""Add Popcorn"" button" +552 importButter not recognizing event types/info +489 default editor breaks with youtube player +421 Fix linting errors +329 Up/Down arrows should move to next/prev event +514 resizing track events does not update start or end time +432 Track events can't lose focus +243 Create a rudimentary testing process +443 Butter should always reliably load +538 Confirm that single press of esc key dismisses event editors +271 Weird design bug in chrome +498 Tests fail first pass through on Chrome +441 Scubber Line Behaviour erratic. +553 `node make` doesn't work +500 Start using selenium +231 Replace Makefiles with shelljs +492 Timebar canvas is too large +301 Use the require order plugin +527 Remove plugin addition from template.js +485 trackevent timing buggy +526 Tests broken as a result of merge +506 Remove jquery and jquery-ui +218 bodyReady crashes on SVG elements +502 util/time doesn't have license header +516 Fix tests after DragNDrop broke them +481 uncaught typeError in Chrome only +461 Move editor dialogues to tray, create quick edit flow +505 Replace $sortable +94 Dragging the scrubber/a track event along the timeline does not force the timeline to scroll +490 Zooming doesn't move the scrubber +477 Dragging Footnote in template.html to the edge of track 2 causes it to disappear +455 "Stop target from ""blinking"" if mouse is dragging something" +342 We should roll out our own drag & drop +404 Clean up start-up and organization of Butter +262 Object has no method 'slider' issue still around +314 Changes to track list UI +133 "Make ""standard target"" default target" +493 If a track event is double clicked and the editor is already opened, focus the editor. +474 Update Popcorn.js to v1.2 +408 Attempt to clean up timeline module +487 "Use ""on"" instead of ""listen"" for Popcorn interface" +480 Get rid of Butter instancing +488 Remove CSS checking in plugin module +348 Fix module order dependency +429 Style Guide for Source +352 Performance when zooming/moving around on timeline is slow with numerous trackevents +132 Footnote data stays in workspace preview too long +130 """Subtitle-container"" is confusing" +45 resizing page UI problem +131 Export>Preview not working +107 google maps exception in butter +105 adding tracks doesn't fire timeupdate on youtube +121 Image plugin broken on experimental branch +134 Youtube doesn't export/preview +104 Clicking New Project should re-open the start dialog +102 Hide youtube's native flash controls and annotations by default +96 Scrubber doesn't reset when time is reset +139 You should be able to set the project title when you create it +106 Youtube seek bar fires play +127 Selection needs to be turned off for Chrome +140 Track events broken in chrome +64 Reassign track representation for manual track -> DOM assignment +112 image and webpage refresh issue +72 Show one object in preview window +71 DOM Object database for tracks +70 Enable the user of arbitrary player plugins +63 Implement timeline using CSS +118 Review - Import, Export, Load, Save, CSS, Player Plugins, DOM Database, Preview Isolation +109 Wordriver plug-in start doesn't fire +69 Click & Drag plugins to create events +67 Project Details +85 what to do on a refresh +84 Remove butter.html +97 $popcorn is undefined when resizing window +92 Update AUTHORS +90 Changing url for media needs to actually change the media +66 Help Button +88 Video continues to play when red timeline bar is held +68 Remove accordion +17 Play/Pause should be controllable with the space bar. +35 Focus on one command at a time in preview pane +18 Better handles on the timeline to grab each instance of a plugin +49 UI Revamp +36 Youtube support in Butter? +22 Multiple timelines per plugin + +0.2 Release, "Ghostbusters" - March 1, 2012 + +337 Ability to login/save/load from a cornfield server +390 Setting trackEvent out time > video duration causes issues +268 Fade in of modal dialogues far too slow +332 Ensure all player types work +427 Better UX for highlighting elements +446 Warning when closing a window dialog manually +428 Change per-file licenses +395 Butter readme should include info on submodules +454 Flash target on click, not hover +438 Tray UI should be no bigger than is necessary to accomodate 4 tracks +256 Timecodes should round to 3 decimal places +449 ";c=!!b.insertBefore(c.lastChild,b.firstChild)}j||(c=!l(a));if(c)a.documentShived=c;return a}var d=g.html5||{},n=/^<|^(?:button|form|map|select|textarea|object|iframe|option|optgroup)$/i, +m=/^<|^(?:a|b|button|code|div|fieldset|form|h1|h2|h3|h4|h5|h6|i|iframe|img|input|label|li|link|ol|option|p|param|q|script|select|span|strong|style|table|tbody|td|textarea|tfoot|th|thead|tr|ul)$/i,i,j;(function(){var a=b.createElement("a");a.innerHTML="";i="hidden"in a;if(!(a=1==a.childNodes.length))a:{try{b.createElement("a")}catch(c){a=!0;break a}a=b.createDocumentFragment();a="undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}j= +a})();var e={elements:d.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:!1!==d.shivCSS,shivMethods:!1!==d.shivMethods,type:"default",shivDocument:h};g.html5=e;h(b)})(this,document); diff --git a/public/media/js/jcarousellite_1.0.1.min.js b/public/media/js/jcarousellite_1.0.1.min.js new file mode 100644 index 00000000..f16734ae --- /dev/null +++ b/public/media/js/jcarousellite_1.0.1.min.js @@ -0,0 +1 @@ +(function($){$.fn.jCarouselLite=function(o){o=$.extend({btnPrev:null,btnNext:null,btnGo:null,mouseWheel:false,auto:null,speed:200,easing:null,vertical:false,circular:true,visible:3,start:0,scroll:1,beforeStart:null,afterEnd:null},o||{});return this.each(function(){var b=false,animCss=o.vertical?"top":"left",sizeCss=o.vertical?"height":"width";var c=$(this),ul=$("ul",c),tLi=$("li",ul),tl=tLi.size(),v=o.visible;if(o.circular){ul.prepend(tLi.slice(tl-v-1+1).clone()).append(tLi.slice(0,v).clone());o.start+=v}var f=$("li",ul),itemLength=f.size(),curr=o.start;c.css("visibility","visible");f.css({overflow:"hidden",float:o.vertical?"none":"left"});ul.css({margin:"0",padding:"0",position:"relative","list-style-type":"none","z-index":"1"});c.css({overflow:"hidden",position:"relative","z-index":"2",left:"0px"});var g=o.vertical?height(f):width(f);var h=g*itemLength;var j=g*v;f.css({width:f.width(),height:f.height()});ul.css(sizeCss,h+"px").css(animCss,-(curr*g));c.css(sizeCss,j+"px");if(o.btnPrev)$(o.btnPrev).click(function(){return go(curr-o.scroll)});if(o.btnNext)$(o.btnNext).click(function(){return go(curr+o.scroll)});if(o.btnGo)$.each(o.btnGo,function(i,a){$(a).click(function(){return go(o.circular?o.visible+i:i)})});if(o.mouseWheel&&c.mousewheel)c.mousewheel(function(e,d){return d>0?go(curr-o.scroll):go(curr+o.scroll)});if(o.auto)setInterval(function(){go(curr+o.scroll)},o.auto+o.speed);function vis(){return f.slice(curr).slice(0,v)};function go(a){if(!b){if(o.beforeStart)o.beforeStart.call(this,vis());if(o.circular){if(a<=o.start-v-1){ul.css(animCss,-((itemLength-(v*2))*g)+"px");curr=a==o.start-v-1?itemLength-(v*2)-1:itemLength-(v*2)-o.scroll}else if(a>=itemLength-v+1){ul.css(animCss,-((v)*g)+"px");curr=a==itemLength-v+1?v+1:v+o.scroll}else curr=a}else{if(a<0||a>itemLength-v)return;else curr=a}b=true;ul.animate(animCss=="left"?{left:-(curr*g)}:{top:-(curr*g)},o.speed,o.easing,function(){if(o.afterEnd)o.afterEnd.call(this,vis());b=false});if(!o.circular){$(o.btnPrev+","+o.btnNext).removeClass("disabled");$((curr-o.scroll<0&&o.btnPrev)||(curr+o.scroll>itemLength-v&&o.btnNext)||[]).addClass("disabled")}}return false}})};function css(a,b){return parseInt($.css(a[0],b))||0};function width(a){return a[0].offsetWidth+css(a,'marginLeft')+css(a,'marginRight')};function height(a){return a[0].offsetHeight+css(a,'marginTop')+css(a,'marginBottom')}})(jQuery); \ No newline at end of file diff --git a/public/popcornlogo-small.png b/public/popcornlogo-small.png new file mode 100644 index 00000000..bc21655a Binary files /dev/null and b/public/popcornlogo-small.png differ diff --git a/public/struggling-dino.gif b/public/struggling-dino.gif new file mode 100644 index 00000000..dce11d4e Binary files /dev/null and b/public/struggling-dino.gif differ diff --git a/resources/bg.jpg b/resources/bg.jpg new file mode 100644 index 00000000..2cb56189 Binary files /dev/null and b/resources/bg.jpg differ diff --git a/resources/buttons.png b/resources/buttons.png new file mode 100644 index 00000000..048d2f93 Binary files /dev/null and b/resources/buttons.png differ diff --git a/resources/controls/controls_icon_playPause.png b/resources/controls/controls_icon_playPause.png new file mode 100644 index 00000000..653e9d7b Binary files /dev/null and b/resources/controls/controls_icon_playPause.png differ diff --git a/resources/controls/controls_icons.png b/resources/controls/controls_icons.png new file mode 100644 index 00000000..25727a3d Binary files /dev/null and b/resources/controls/controls_icons.png differ diff --git a/resources/controls/controls_time_arrowL.gif b/resources/controls/controls_time_arrowL.gif new file mode 100644 index 00000000..da4c88da Binary files /dev/null and b/resources/controls/controls_time_arrowL.gif differ diff --git a/resources/controls/controls_time_arrowR.gif b/resources/controls/controls_time_arrowR.gif new file mode 100644 index 00000000..9f469c36 Binary files /dev/null and b/resources/controls/controls_time_arrowR.gif differ diff --git a/resources/controls/embed_size_sprite.png b/resources/controls/embed_size_sprite.png new file mode 100644 index 00000000..10fbbf5a Binary files /dev/null and b/resources/controls/embed_size_sprite.png differ diff --git a/resources/controls/icon_play.png b/resources/controls/icon_play.png new file mode 100644 index 00000000..7e32ba0e Binary files /dev/null and b/resources/controls/icon_play.png differ diff --git a/resources/controls/postroll_logo.png b/resources/controls/postroll_logo.png new file mode 100644 index 00000000..89361715 Binary files /dev/null and b/resources/controls/postroll_logo.png differ diff --git a/resources/default-icon.png b/resources/default-icon.png new file mode 100644 index 00000000..cd7eb816 Binary files /dev/null and b/resources/default-icon.png differ diff --git a/resources/drop.png b/resources/drop.png new file mode 100644 index 00000000..736ecfa7 Binary files /dev/null and b/resources/drop.png differ diff --git a/resources/embed-icons-50x50.png b/resources/embed-icons-50x50.png new file mode 100644 index 00000000..027b65f0 Binary files /dev/null and b/resources/embed-icons-50x50.png differ diff --git a/resources/feedback-icon.png b/resources/feedback-icon.png new file mode 100644 index 00000000..f5d9b0ab Binary files /dev/null and b/resources/feedback-icon.png differ diff --git a/resources/fonts/BEBAS___-webfont.ttf b/resources/fonts/BEBAS___-webfont.ttf new file mode 100755 index 00000000..bb74dd7c Binary files /dev/null and b/resources/fonts/BEBAS___-webfont.ttf differ diff --git a/resources/fonts/BEBAS___-webfont.woff b/resources/fonts/BEBAS___-webfont.woff new file mode 100755 index 00000000..cf0fa4ce Binary files /dev/null and b/resources/fonts/BEBAS___-webfont.woff differ diff --git a/resources/fonts/opensans-bold-webfont.ttf b/resources/fonts/opensans-bold-webfont.ttf new file mode 100755 index 00000000..1340fd8d Binary files /dev/null and b/resources/fonts/opensans-bold-webfont.ttf differ diff --git a/resources/fonts/opensans-bold-webfont.woff b/resources/fonts/opensans-bold-webfont.woff new file mode 100755 index 00000000..941993a7 Binary files /dev/null and b/resources/fonts/opensans-bold-webfont.woff differ diff --git a/resources/fonts/opensans-bolditalic-webfont.ttf b/resources/fonts/opensans-bolditalic-webfont.ttf new file mode 100755 index 00000000..b560328d Binary files /dev/null and b/resources/fonts/opensans-bolditalic-webfont.ttf differ diff --git a/resources/fonts/opensans-bolditalic-webfont.woff b/resources/fonts/opensans-bolditalic-webfont.woff new file mode 100755 index 00000000..93b3ea37 Binary files /dev/null and b/resources/fonts/opensans-bolditalic-webfont.woff differ diff --git a/resources/fonts/opensans-italic-webfont.ttf b/resources/fonts/opensans-italic-webfont.ttf new file mode 100755 index 00000000..88405894 Binary files /dev/null and b/resources/fonts/opensans-italic-webfont.ttf differ diff --git a/resources/fonts/opensans-italic-webfont.woff b/resources/fonts/opensans-italic-webfont.woff new file mode 100755 index 00000000..399e26ba Binary files /dev/null and b/resources/fonts/opensans-italic-webfont.woff differ diff --git a/resources/fonts/opensans-regular-webfont.ttf b/resources/fonts/opensans-regular-webfont.ttf new file mode 100755 index 00000000..8f13083e Binary files /dev/null and b/resources/fonts/opensans-regular-webfont.ttf differ diff --git a/resources/fonts/opensans-regular-webfont.woff b/resources/fonts/opensans-regular-webfont.woff new file mode 100755 index 00000000..84c23742 Binary files /dev/null and b/resources/fonts/opensans-regular-webfont.woff differ diff --git a/resources/glyphicons-halflings-alt.png b/resources/glyphicons-halflings-alt.png new file mode 100644 index 00000000..ba67cf38 Binary files /dev/null and b/resources/glyphicons-halflings-alt.png differ diff --git a/resources/glyphicons-halflings-green.png b/resources/glyphicons-halflings-green.png new file mode 100644 index 00000000..71696ecf Binary files /dev/null and b/resources/glyphicons-halflings-green.png differ diff --git a/resources/glyphicons-halflings-grey.png b/resources/glyphicons-halflings-grey.png new file mode 100644 index 00000000..b33b3f1b Binary files /dev/null and b/resources/glyphicons-halflings-grey.png differ diff --git a/resources/glyphicons-halflings-white.png b/resources/glyphicons-halflings-white.png new file mode 100644 index 00000000..95c931b6 Binary files /dev/null and b/resources/glyphicons-halflings-white.png differ diff --git a/resources/glyphicons-halflings.png b/resources/glyphicons-halflings.png new file mode 100644 index 00000000..a6e06084 Binary files /dev/null and b/resources/glyphicons-halflings.png differ diff --git a/resources/icons/link.png b/resources/icons/link.png new file mode 100644 index 00000000..8e561ed0 Binary files /dev/null and b/resources/icons/link.png differ diff --git a/resources/icons/map.png b/resources/icons/map.png new file mode 100644 index 00000000..773f21ed Binary files /dev/null and b/resources/icons/map.png differ diff --git a/resources/icons/ticket.png b/resources/icons/ticket.png new file mode 100644 index 00000000..d7a45071 Binary files /dev/null and b/resources/icons/ticket.png differ diff --git a/resources/image-icon.png b/resources/image-icon.png new file mode 100644 index 00000000..6cf8bf56 Binary files /dev/null and b/resources/image-icon.png differ diff --git a/resources/logo.png b/resources/logo.png new file mode 100644 index 00000000..f4b5adc7 Binary files /dev/null and b/resources/logo.png differ diff --git a/resources/logo/logo-100x45.png b/resources/logo/logo-100x45.png new file mode 100644 index 00000000..1c7c01f4 Binary files /dev/null and b/resources/logo/logo-100x45.png differ diff --git a/resources/logo/logo-100x45@x2.png b/resources/logo/logo-100x45@x2.png new file mode 100644 index 00000000..3532bf30 Binary files /dev/null and b/resources/logo/logo-100x45@x2.png differ diff --git a/resources/logo/popcorn-inner.png b/resources/logo/popcorn-inner.png new file mode 100644 index 00000000..550761c0 Binary files /dev/null and b/resources/logo/popcorn-inner.png differ diff --git a/resources/logo/popcorn-outer.png b/resources/logo/popcorn-outer.png new file mode 100644 index 00000000..16aebffc Binary files /dev/null and b/resources/logo/popcorn-outer.png differ diff --git a/resources/logo/popcorn.png b/resources/logo/popcorn.png new file mode 100644 index 00000000..70b85ae4 Binary files /dev/null and b/resources/logo/popcorn.png differ diff --git a/resources/media-icons.png b/resources/media-icons.png new file mode 100644 index 00000000..96d9d319 Binary files /dev/null and b/resources/media-icons.png differ diff --git a/resources/popcorn-icon.png b/resources/popcorn-icon.png new file mode 100644 index 00000000..0e7e3341 Binary files /dev/null and b/resources/popcorn-icon.png differ diff --git a/resources/spinny.gif b/resources/spinny.gif new file mode 100644 index 00000000..ce9b14f2 Binary files /dev/null and b/resources/spinny.gif differ diff --git a/resources/sprite.png b/resources/sprite.png new file mode 100644 index 00000000..292321e2 Binary files /dev/null and b/resources/sprite.png differ diff --git a/resources/status-bg.png b/resources/status-bg.png new file mode 100644 index 00000000..f5b91cdf Binary files /dev/null and b/resources/status-bg.png differ diff --git a/resources/track-bg.png b/resources/track-bg.png new file mode 100644 index 00000000..f5b91cdf Binary files /dev/null and b/resources/track-bg.png differ diff --git a/resources/video.png b/resources/video.png new file mode 100644 index 00000000..100bc7f8 Binary files /dev/null and b/resources/video.png differ diff --git a/resources/webmaker/webmaker-main_01.png b/resources/webmaker/webmaker-main_01.png new file mode 100644 index 00000000..2001f4eb Binary files /dev/null and b/resources/webmaker/webmaker-main_01.png differ diff --git a/resources/webmaker/webmaker-main_02.png b/resources/webmaker/webmaker-main_02.png new file mode 100644 index 00000000..ed8c7c5b Binary files /dev/null and b/resources/webmaker/webmaker-main_02.png differ diff --git a/resources/webmaker/webmaker-main_03.png b/resources/webmaker/webmaker-main_03.png new file mode 100644 index 00000000..d4e8076f Binary files /dev/null and b/resources/webmaker/webmaker-main_03.png differ diff --git a/resources/webmaker/webmaker-main_04.png b/resources/webmaker/webmaker-main_04.png new file mode 100644 index 00000000..650cd6bd Binary files /dev/null and b/resources/webmaker/webmaker-main_04.png differ diff --git a/src/butter.js b/src/butter.js new file mode 100644 index 00000000..6079498d --- /dev/null +++ b/src/butter.js @@ -0,0 +1,50 @@ +/*! This Source Code Form is subject to the terms of the MIT license + * If a copy of the MIT license was not distributed with this file, you can + * obtain one at http://www.mozillapopcorn.org/butter-license.txt */ + +/*jshint evil: true */ + +/** + * In the source case, use document.write to write out the require tag, + * and load all moduels as distinct scripts for debugging. After a build, + * all the modules are inlined, so will not use the document.write path. + * Use has() testing module, since the requirejs optimizer will convert + * the has test to false, and minification will strip the false code + * branch. http://requirejs.org/docs/optimization.html#hasjs + */ +(function () { + // Stub for has function. + function has() { + return true; + } + + var Butter = {}; + + Butter.init = function() { + if ( !Butter.__waiting ) { + Butter.__waiting = []; + } + Butter.__waiting.push( arguments ); + }; + + if ( !window.Butter ) { + window.Butter = Butter; + } + + if ( has( 'source-config' ) ) { + // Get the location of the butter source. + // The last script tag should be the butter source + // tag since in dev, it will be a blocking script tag, + // so latest tag is the one for this script. + var scripts = document.getElementsByTagName( 'script' ), + path = scripts[scripts.length - 1].src; + path = path.split( '/' ); + path.pop(); + path = path.join( '/' ) + '/'; + + if ( !window.require ) { + document.write( ' + + + + + + +

Popcorn Popup Plug-in Demo

+

Allows for cool cool things to popup on the screen and be position around the video. Types include Popup, Thought, Speech, Did You Know and Fact

+ +
+ +
+
+ + + diff --git a/templates/assets/plugins/popup/popcorn.popup.js b/templates/assets/plugins/popup/popcorn.popup.js new file mode 100644 index 00000000..a087c22b --- /dev/null +++ b/templates/assets/plugins/popup/popcorn.popup.js @@ -0,0 +1,501 @@ +// PLUGIN: Popup + +(function ( Popcorn ) { + + var sounds = {}, + events = [], + soundIndex = 0, + MAX_AUDIO_TIME = 2, + _pluginRoot = location.protocol + "//" + location.hostname + ( location.port ? ":" + location.port : "" ) + "/templates/assets/plugins/popup/", + FILL_STYLE = "rgb(255, 255, 255)", + innerDivTriangles = {}, + DEFAULT_FONT = "Tangerine"; + + // Set up speech innerDiv triangles + innerDivTriangles.speech = document.createElement( "canvas" ); + innerDivTriangles.thought = document.createElement( "canvas" ); + + // Creates a triangle for a speech innerDiv + function drawSpeech( canvas, lineWidth ) { + var ctx = canvas.getContext( "2d" ); + ctx.save(); + ctx.beginPath(); + ctx.moveTo(0.4, 0.3); + ctx.bezierCurveTo(0.4, 0.3, 17.8, 26.3, 15.1, 41.9); + ctx.bezierCurveTo(15.1, 41.9, 26.2, 26.3, 23.4, 0.3); + ctx.fillStyle = FILL_STYLE; + ctx.fill(); + ctx.lineWidth = lineWidth; + ctx.stroke(); + ctx.restore(); + } + + // Creates three innerDivs for a "thought" speech innerDiv + function drawThought( canvas, lineWidth ) { + var ctx = canvas.getContext( "2d" ); + // circle1 + ctx.save(); + ctx.beginPath(); + ctx.moveTo(13.5, 7.0); + ctx.bezierCurveTo(13.5, 10.6, 10.6, 13.5, 7.0, 13.5); + ctx.bezierCurveTo(3.4, 13.5, 0.5, 10.6, 0.5, 7.0); + ctx.bezierCurveTo(0.5, 3.4, 3.4, 0.5, 7.0, 0.5); + ctx.bezierCurveTo(10.6, 0.5, 13.5, 3.4, 13.5, 7.0); + ctx.closePath(); + ctx.fillStyle = FILL_STYLE; + ctx.fill(); + ctx.lineWidth = lineWidth; + ctx.stroke(); + + // circle2 + ctx.beginPath(); + ctx.moveTo(17.5, 23.8); + ctx.bezierCurveTo(17.5, 26.1, 15.6, 28.0, 13.2, 28.0); + ctx.bezierCurveTo(10.9, 28.0, 9.0, 26.1, 9.0, 23.8); + ctx.bezierCurveTo(9.0, 21.4, 10.9, 19.5, 13.2, 19.5); + ctx.bezierCurveTo(15.6, 19.5, 17.5, 21.4, 17.5, 23.8); + ctx.closePath(); + ctx.fill(); + ctx.lineWidth = lineWidth; + ctx.stroke(); + + // circle3 + ctx.beginPath(); + ctx.moveTo(27.5, 31.8); + ctx.bezierCurveTo(27.5, 33.5, 26.0, 35.0, 24.2, 35.0); + ctx.bezierCurveTo(22.5, 35.0, 21.0, 33.5, 21.0, 31.8); + ctx.bezierCurveTo(21.0, 30.0, 22.5, 28.5, 24.2, 28.5); + ctx.bezierCurveTo(26.0, 28.5, 27.5, 30.0, 27.5, 31.8); + ctx.closePath(); + ctx.fill(); + ctx.lineWidth = lineWidth; + ctx.stroke(); + ctx.restore(); + } + + drawSpeech( innerDivTriangles.speech, 2 ); + drawThought( innerDivTriangles.thought, 2 ); + + Popcorn.plugin( "popup", { + manifest: { + about: { + name: "Popcorn Maker Popup Plugin", + version: "0.1", + author: "Kate Hudson @k88hudson, Matthew Schranz @mjschranz, Brian Chirls @bchirls", + website: "http://github.com/k88hudson, http://github.com/mjschranz, https://github.com/brianchirls/" + }, + options: { + start: { + elem: "input", + type: "number", + label: "In", + "units": "seconds" + }, + end: { + elem: "input", + type: "number", + label: "Out", + "units": "seconds" + }, + text: { + elem: "textarea", + label: "Text", + "default": "Pop!" + }, + linkUrl: { + elem: "input", + type: "url", + label: "Link URL" + }, + type: { + elem: "select", + options: [ "Popup", "Speech", "Thought Bubble" ], + values: [ "popup", "speech", "thought" ], + label: "Type", + "default": "popup" + }, + triangle: { + elem: "select", + options: [ "Top Left", "Top Right", "Bottom Left", "Bottom Right" ], + values: [ "top left", "top right", "bottom left", "bottom right" ], + label: "Tail Position", + "default": "bottom left", + optional: true + }, + sound: { + elem: "input", + type: "checkbox", + label: "Sound", + "default": false, + optional: true + }, + icon: { + elem: "select", + options: [ "Error", "Audio", "Broken Heart", "Cone", "Earth", + "Eye", "Heart", "Info", "Man", "Money", "Music", "Net", + "Skull", "Star", "Thumbs Down", "Thumbs Up", "Time", + "Trophy", "Tv", "User", "Virus", "Women" ], + values: [ "error", "audio", "brokenheart", "cone", "earth", + "eye", "heart", "info", "man", "money", "music", "net", + "skull", "star", "thumbsdown", "thumbsup", "time", + "trophy", "tv", "user", "virus", "women" ], + label: "Pop Icon", + "default": "error", + optional: true + }, + flip: { + elem: "input", + type: "checkbox", + label: "Flip Tail?", + "default": false, + optional: true + }, + top: { + elem: "input", + type: "number", + label: "Top", + units: "%", + "default": 5, + hidden: true + }, + left: { + elem: "input", + type: "number", + label: "Left", + units: "%", + "default": 20, + hidden: true + }, + width: { + elem: "input", + type: "number", + units: "%", + label: "Width", + "default": 30, + hidden: true + }, + transition: { + elem: "select", + options: [ "None", "Pop", "Fade", "Slide Up", "Slide Down" ], + values: [ "popcorn-none", "popcorn-pop", "popcorn-fade", "popcorn-slide-up", "popcorn-slide-down" ], + label: "Transition", + "default": "popcorn-pop" + }, + fontFamily: { + elem: "select", + label: "Font", + styleClass: "", + googleFonts: true, + "default": "Merriweather", + group: "advanced" + }, + fontSize: { + elem: "input", + type: "number", + label: "Font Size", + "default": 12, + units: "px", + group: "advanced" + }, + fontColor: { + elem: "input", + type: "color", + label: "Font colour", + "default": "#668B8B", + group: "advanced" + }, + fontDecorations: { + elem: "checkbox-group", + labels: { bold: "Bold", italics: "Italics", underline: "Underline" }, + "default": { bold: false, italics: false, underline: false }, + group: "advanced" + }, + zindex: { + hidden: true + } + } + }, + + _setup: function( options ) { + + var target = document.getElementById( options.target ), + container = document.createElement( "div" ), + context = this, + audio, + width = options.width + "%", + top = options.top + "%", + left = options.left + "%", + i, + fontSheet, + originalFamily = options.fontFamily, + flip = options.flip ? " flip" : "", + innerDiv = document.createElement( "div" ), + textContainer = document.createElement( "div" ), + text = options.text, + node = document.createElement( "span" ), + link = document.createElement( "a" ), + linkUrl = options.linkUrl, + img, + TRIANGLE_WIDTH = 40, + TRIANGLE_HEIGHT = 60; + + if ( !target ) { + target = context.media.parentNode; + } + + options._target = target; + + function selectAudio( id, sources ) { + var i, j, event, diff, + eligibleAudio, + audio, + source; + + function resetAudio() { + this.currentTime = 0; + this.pause(); + } + + if ( !sounds[ id ] ) { + audio = document.createElement( "audio" ); + for ( i = 0; i < sources.length; i ++ ) { + source = document.createElement( "source" ); + source.src = _pluginRoot + sources[ i ]; + audio.appendChild( source ); + } + audio.id = "popcorn-pop-sound-" + soundIndex; + soundIndex++; + audio.preload = true; + audio.style.display = "none"; + audio.addEventListener( "ended", resetAudio, false ); + + document.body.appendChild( audio ); + sounds[ id ] = [ audio ]; + return audio; + } + + audio = sounds[ id ][ 0 ]; + if ( audio.duration ) { + diff = Math.min( audio.duration, MAX_AUDIO_TIME ); + } else { + diff = MAX_AUDIO_TIME; + } + + //make sure there are no other events using this sound at the same time + eligibleAudio = sounds[ id ].slice( 0 ); + for ( i = 0; i < events.length; i++ ) { + event = events[ i ]; + if ( event.sound === options.sound && + event.start <= options.start + diff && + event.start + diff >= options.start ) { + + j = eligibleAudio.indexOf( event.audio ); + if ( j >= 0 ) { + eligibleAudio.splice( j, 1 ); + } + } + } + + if ( eligibleAudio.length ) { + audio = eligibleAudio[ 0 ]; + } else { + audio = sounds[ id ][ 0 ].cloneNode( true ); + audio.id = "popcorn-pop-sound-" + soundIndex; + soundIndex++; + + // not sure whether cloning copies the events in all browsers, + // so remove it and add again just in case + audio.removeEventListener( "ended", resetAudio, false ); + audio.addEventListener( "ended", resetAudio, false ); + + document.body.appendChild( audio ); + sounds[ id ].push( audio ); + } + + return audio; + } + + function makeTriangle( innerDiv ) { + + var triangle, + ctx; + + //Set the base classes + innerDiv.className = "speechBubble " + options.type + " " + options.triangle + " " + flip; + + triangle = document.createElement( "canvas" ); + ctx = triangle.getContext( "2d" ); + + triangle.width = TRIANGLE_WIDTH; + triangle.height = TRIANGLE_HEIGHT; + triangle.className = "canvas"; + innerDiv.appendChild( triangle ); + + //Draw according to the style + if ( options.type === "speech" ) { + triangle.getContext( "2d" ).drawImage( innerDivTriangles.speech, 0, 0 ); + } + if ( options.type === "thought" ) { + triangle.getContext( "2d" ).drawImage( innerDivTriangles.thought, 0, 0 ); + } + } //makeTriangle + + container.style.position = "absolute"; + container.style.top = top; + container.style.left = left; + container.style.width = width; + container.style.zIndex = +options.zindex; + + innerDiv = document.createElement( "div" ); + textContainer = document.createElement( "div" ); + + textContainer.style.fontStyle = options.fontDecorations.italics ? "italic" : "normal"; + textContainer.style.color = options.fontColor ? options.fontColor : "#668B8B"; + textContainer.style.textDecoration = options.fontDecorations.underline ? "underline" : "none"; + textContainer.style.fontSize = options.fontSize ? options.fontSize + "px" : "12px"; + textContainer.style.fontWeight = options.fontDecorations.bold ? "bold" : "normal"; + + text = text.split( /[\n\r]/ ); + for ( i = 0; i < text.length; i++ ) { + node = document.createElement( "span" ); + node.appendChild( document.createTextNode( text[ i ] ) ); + + if ( linkUrl ) { + if ( i ) { + link.appendChild( document.createElement( "br" ) ); + } + link.appendChild( node ); + } else { + if ( i ) { + textContainer.appendChild( document.createElement( "br" ) ); + } + textContainer.appendChild( node ); + } + } + + if ( linkUrl ) { + link.href = linkUrl; + link.target = "_blank"; + + link.addEventListener( "click", function( e ) { + context.media.pause(); + }, false ); + + link.style.color = options.fontColor; + + textContainer.appendChild( link ); + } + + innerDiv.appendChild( textContainer ); + container.appendChild( innerDiv ); + + if ( options.type === "popup" ) { + innerDiv.classList.add( "popup-inner-div" ); + container.classList.add( "popcorn-popup" ); + + if ( options.icon ) { + img = document.createElement( "img" ); + img.setAttribute( "class", "popup-icon" ); + img.addEventListener( "load", function() { + var width = img.width || img.naturalWidth, + height = img.height || img.naturalHeight; + + if ( height > 60 ) { + width = 60 * width / height; + height = 60; + img.style.width = width + "px"; + } + + img.style.left = -( width - 16 ) + "px"; + + // make sure container is still non-null + // if _teardown is called too quickly, it will become null before img loads + if ( container ){ + if ( container.offsetHeight ) { + img.style.top = ( container.offsetHeight - height ) / 2 - 4 + "px"; + } + container.insertBefore( img, container.firstChild ); + } + }, false ); + img.src = _pluginRoot + "images/" + options.icon + ".png"; + } + + //load up sound. + if ( options.sound ) { + if ( !audio ) { + audio = selectAudio( "popup", [ "sounds/mouthpop.ogg", "sounds/mouthpop.wav" ] ); + options.audio = audio; + } + } + } + else { + makeTriangle( innerDiv ); + } + + // Add transition + container.classList.add( options.transition ); + container.classList.add( "off" ); + target.appendChild( container ); + options._container = container; + + fontSheet = document.createElement( "link" ); + fontSheet.rel = "stylesheet"; + fontSheet.type = "text/css"; + options.fontFamily = options.fontFamily ? options.fontFamily : options._natives.manifest.options.fontFamily[ "default" ]; + // Store reference to generated sheet for removal later, remove any existing ones + options._fontSheet = fontSheet; + document.head.appendChild( fontSheet ); + + fontSheet.onload = function ( e ) { + // Apply all the styles + textContainer.style.fontFamily = options.fontFamily ? originalFamily : DEFAULT_FONT; + }; + fontSheet.href = "http://fonts.googleapis.com/css?family=" + options.fontFamily.replace( /\s/g, "+" ); + + options.toString = function() { + return options.text || options._natives.manifest.options.text[ "default" ]; + }; + }, + + start: function( event, options ) { + var audio = options.audio, + video = this.media; + + if ( options._container ) { + options._container.classList.add( "on" ); + options._container.classList.remove( "off" ); + } + + if ( audio && audio.duration && !video.paused && + video.currentTime - 1 < options.start ) { + + audio.volume = video.volume; + audio.muted = video.muted; + audio.play(); + if ( !audio.duration || isNaN( audio.duration ) || audio.duration > MAX_AUDIO_TIME ) { + setTimeout(function() { + audio.currentTime = 0; + audio.pause(); + }, MAX_AUDIO_TIME ); + } + } + }, + + end: function( event, options ) { + if ( options._container ) { + options._container.classList.add( "off" ); + options._container.classList.remove( "on" ); + } + }, + + _teardown: function( options ) { + if ( options._container && options._target ) { + options._target.removeChild( options._container ); + } + + if ( options._fontSheet ) { + document.head.removeChild( options._fontSheet ); + } + } + }); +}( Popcorn )); diff --git a/templates/assets/plugins/popup/popup-icon.png b/templates/assets/plugins/popup/popup-icon.png new file mode 100644 index 00000000..9fe9f982 Binary files /dev/null and b/templates/assets/plugins/popup/popup-icon.png differ diff --git a/templates/assets/plugins/popup/sounds/mouthpop.ogg b/templates/assets/plugins/popup/sounds/mouthpop.ogg new file mode 100644 index 00000000..12c8dd38 Binary files /dev/null and b/templates/assets/plugins/popup/sounds/mouthpop.ogg differ diff --git a/templates/assets/plugins/popup/sounds/mouthpop.wav b/templates/assets/plugins/popup/sounds/mouthpop.wav new file mode 100644 index 00000000..480da6f1 Binary files /dev/null and b/templates/assets/plugins/popup/sounds/mouthpop.wav differ diff --git a/templates/assets/plugins/popup/thumbsdown.png b/templates/assets/plugins/popup/thumbsdown.png new file mode 100644 index 00000000..3e3d1d63 Binary files /dev/null and b/templates/assets/plugins/popup/thumbsdown.png differ diff --git a/templates/assets/plugins/popup/thumbsup.png b/templates/assets/plugins/popup/thumbsup.png new file mode 100644 index 00000000..102bd82a Binary files /dev/null and b/templates/assets/plugins/popup/thumbsup.png differ diff --git a/templates/assets/plugins/skip/popcorn.skip.js b/templates/assets/plugins/skip/popcorn.skip.js new file mode 100644 index 00000000..f3b22bd0 --- /dev/null +++ b/templates/assets/plugins/skip/popcorn.skip.js @@ -0,0 +1,49 @@ +(function( Popcorn ) { + Popcorn.plugin( "skip", function() { + + return { + _setup: function( options ) { + var skipTime = options.end; + + options.skipRange = function() { + var ct = this.currentTime(); + if ( ct > options.start && ct < options.end ) { + this.currentTime( skipTime ); + } + }; + options.toString = function() { + return "Skip"; + }; + + this.on( "timeupdate", options.skipRange ); + + }, + start: function( event, options ) { + }, + end: function( event, options ) { + }, + _teardown: function( options ) { + this.off( "timeupdate", options.skipRange ); + } + }; + }, + { + "options": { + "start": { + "elem": "input", + "type": "text", + "label": "In", + "units": "seconds" + }, + "end": { + "elem": "input", + "type": "text", + "label": "Out", + "units": "seconds" + }, + "target": { + "hidden": true + } + } + }); +}( Popcorn )); diff --git a/templates/assets/plugins/skip/skip-icon.png b/templates/assets/plugins/skip/skip-icon.png new file mode 100644 index 00000000..18932366 Binary files /dev/null and b/templates/assets/plugins/skip/skip-icon.png differ diff --git a/templates/assets/plugins/text/popcorn.text.css b/templates/assets/plugins/text/popcorn.text.css new file mode 100644 index 00000000..2b5a563a --- /dev/null +++ b/templates/assets/plugins/text/popcorn.text.css @@ -0,0 +1,88 @@ +.popcorn-text { + color: #FFF; +} + +.popcorn-text.off { + visibility: hidden; +} + +.text-inner-div a { + text-decoration: none; +} +.text-inner-div a:after { + content: ""; + display: inline-block; + opacity: 0.2; + background-position: -336px -96px; + background-image: url( "../../../../resources/glyphicons-halflings-green.png" ); + width: 14px; + height: 14px; +} +.text-inner-div a:hover { + text-decoration: underline; +} +.text-inner-div a:hover:after { + opacity: 1; +} + +.text-fixed { + position: absolute; + width: 100%; + top: 0; + bottom: 0; +} +.text-fixed > div { + display: table; + vertical-align: middle; + width: 100%; + height: 100%; + text-align: center; +} + +.text-fixed > div > span { + display: table-cell; + padding: 20px; +} + +.text-fixed > div > span > a { + position: relative; + display: inline-block; +} + +.center > span { + vertical-align: middle; + text-align: center; +} + +.right > span { + vertical-align: middle; + text-align: right; +} + +.left > span { + vertical-align: middle; + text-align: left; +} + +.bottom > span { + vertical-align: bottom; + text-align: center; +} + +.top > span { + vertical-align: top; + text-align: center; +} + +.text-custom { + display: inline-block; + max-width: 500px; +} + +.text-custom > div > span { + padding: 0; +} + +[contenteditable="true"]:focus { + outline: none; +} \ No newline at end of file diff --git a/templates/assets/plugins/text/popcorn.text.js b/templates/assets/plugins/text/popcorn.text.js new file mode 100644 index 00000000..7c913011 --- /dev/null +++ b/templates/assets/plugins/text/popcorn.text.js @@ -0,0 +1,235 @@ +// PLUGIN: text + +(function ( Popcorn ) { + + /** + * text Popcorn plug-in + * Based on popcorn.text.js by @humph + * @param {Object} options + * + * Example: + + **/ + + var DEFAULT_FONT_COLOR = "#000"; + + function normalize( value, minWidth, maxWidth ) { + return Math.max( Math.min( value || 0, maxWidth ), minWidth ); + } + + function newlineToBreak( string ) { + // Deal with both \r\n and \n + return string.replace( /\r?\n/gm, "
" ); + } + + Popcorn.plugin( "text", { + + manifest: { + about: { + name: "Popcorn text Plugin", + version: "0.1", + author: "@k88hudson, @mjschranz" + }, + options: { + text: { + elem: "textarea", + label: "Text", + "default": "Mozilla Popcorn" + }, + linkUrl: { + elem: "input", + type: "url", + label: "Link URL" + }, + position: { + elem: "select", + options: [ "Center", "Bottom", "Left", "Right", "Top", "Custom" ], + values: [ "center", "bottom", "left", "right", "top", "custom" ], + label: "Text Position", + "default": "center" + }, + start: { + elem: "input", + type: "text", + label: "In", + group: "advanced", + "units": "seconds" + }, + end: { + elem: "input", + type: "text", + label: "Out", + group: "advanced", + "units": "seconds" + }, + transition: { + elem: "select", + options: [ "None", "Pop", "Fade", "Slide Up", "Slide Down" ], + values: [ "popcorn-none", "popcorn-pop", "popcorn-fade", "popcorn-slide-up", "popcorn-slide-down" ], + label: "Transition", + "default": "popcorn-fade" + }, + fontFamily: { + elem: "select", + label: "Font", + styleClass: "", + googleFonts: true, + group: "advanced", + "default": "Merriweather" + }, + fontSize: { + elem: "input", + type: "number", + label: "Font Size", + "default": 48, + units: "px", + group: "advanced" + }, + fontColor: { + elem: "input", + type: "color", + label: "Font colour", + "default": DEFAULT_FONT_COLOR, + group: "advanced" + }, + fontDecorations: { + elem: "checkbox-group", + labels: { bold: "Bold", italics: "Italics", underline: "Underline" }, + "default": { bold: false, italics: false, underline: false }, + group: "advanced" + }, + left: { + elem: "input", + type: "number", + label: "Left", + units: "%", + "default": 30, + hidden: true + }, + top: { + elem: "input", + type: "number", + label: "Top", + units: "%", + "default": 30, + hidden: true + }, + zindex: { + hidden: true + } + } + }, + + _setup: function( options ) { + + var target = Popcorn.dom.find( options.target ), + text = newlineToBreak( options.text ), + container = options._container = document.createElement( "div" ), + innerContainer = document.createElement( "div" ), + innerSpan = document.createElement( "span" ), + fontSheet, + fontDecorations = options.fontDecorations || options._natives.manifest.options.fontDecorations[ "default" ], + position = options.position || options._natives.manifest.options.position[ "default" ], + transition = options.transition || options._natives.manifest.options.transition[ "default" ], + link, + context = this; + + if ( !target ) { + target = this.media.parentNode; + } + + options._target = target; + container.style.position = "absolute"; + container.classList.add( "popcorn-text" ); + + if ( position === "custom" ) { + container.classList.add( "text-custom" ); + container.style.left = options.left + "%"; + container.style.top = options.top + "%"; + container.style.zIndex = +options.zindex; + } + else { + container.classList.add( "text-fixed" ); + innerContainer.classList.add( position ); + innerSpan.style.zIndex = +options.zindex; + } + + // Add transition class + options._container.classList.add( transition ); + options._container.classList.add( "off" ); + + // Handle all custom fonts/styling + + options.fontColor = options.fontColor || DEFAULT_FONT_COLOR; + innerContainer.classList.add( "text-inner-div" ); + innerContainer.style.color = options.fontColor; + innerContainer.style.fontStyle = fontDecorations.italics ? "italic" : "normal"; + innerContainer.style.textDecoration = fontDecorations.underline ? "underline" : "none"; + innerContainer.style.fontSize = options.fontSize ? normalize( options.fontSize, 8, 200 ) + "px" : "24px"; + innerContainer.style.fontWeight = fontDecorations.bold ? "bold" : "normal"; + + if ( options.linkUrl ) { + link = document.createElement( "a" ); + link.href = options.linkUrl; + link.target = "_blank"; + link.innerHTML = text; + + link.addEventListener( "click", function( e ) { + context.media.pause(); + }, false ); + + link.style.color = innerContainer.style.color; + + innerSpan.appendChild( link ); + } else { + innerSpan.innerHTML = text; + } + + innerContainer.appendChild( innerSpan ); + container.appendChild( innerContainer ); + target.appendChild( container ); + + fontSheet = document.createElement( "link" ); + fontSheet.rel = "stylesheet"; + fontSheet.type = "text/css"; + options.fontFamily = options.fontFamily ? options.fontFamily : options._natives.manifest.options.fontFamily[ "default" ]; + // Store reference to generated sheet for removal later, remove any existing ones + options._fontSheet = fontSheet; + document.head.appendChild( fontSheet ); + + fontSheet.onload = function ( e ) { + innerContainer.style.fontFamily = options.fontFamily; + }; + fontSheet.href = "http://fonts.googleapis.com/css?family=" + options.fontFamily.replace( /\s/g, "+" ); + + options.toString = function() { + // use the default option if it doesn't exist + return options.text || options._natives.manifest.options.text[ "default" ]; + }; + }, + + start: function( event, options ) { + if ( options._container ) { + options._container.classList.add( "on" ); + options._container.classList.remove( "off" ); + } + }, + + end: function( event, options ) { + if ( options._container ) { + options._container.classList.add( "off" ); + options._container.classList.remove( "on" ); + } + }, + + _teardown: function( options ) { + if ( options._target ) { + options._target.removeChild( options._container ); + } + + if ( options._fontSheet ) { + document.head.removeChild( options._fontSheet ); + } + } + }); +}( window.Popcorn )); diff --git a/templates/assets/plugins/text/text-icon.png b/templates/assets/plugins/text/text-icon.png new file mode 100644 index 00000000..e56a4d4e Binary files /dev/null and b/templates/assets/plugins/text/text-icon.png differ diff --git a/templates/assets/plugins/twitter/popcorn.twitter.css b/templates/assets/plugins/twitter/popcorn.twitter.css new file mode 100644 index 00000000..bdb5954a --- /dev/null +++ b/templates/assets/plugins/twitter/popcorn.twitter.css @@ -0,0 +1,136 @@ +/********************************************************* +* Popcorn Twitter Plugin +*/ +.popcorn-twitter { + position: absolute; + text-align: left; + color: #555; + font-size: 13px; + overflow: hidden; + bottom: 0; + width: 35%; + height: 100%; + background: #FEFEFE; + border-radius: 2px; + background-clip: padding-box; +} +.popcorn-twitter-title { + display: block; + padding: 13px 20px; + margin-right: 10px; + font-size: 16px; + font-weight: 700; + background: #ffffff url("twitter-icon.png") right center no-repeat; + border-bottom: 1px solid #CCC; +} +.popcorn-twitter-tweets { + overflow-y: scroll; + height: 100%; + border-bottom: 1px solid #CCC; +} +.popcorn-twitter ul { + list-style: none; + padding: 0; + margin: 0; +} +.popcorn-twitter a { + text-decoration: none; +} +/* Tweet */ +.popcorn-twitter ul > li { + padding: 10px 20px; + border-bottom: 1px solid #EFEFEF; +} +.popcorn-twitter-tweet-image { + float: left; + margin-right: 5px; + border-radius: 3px; + overflow: hidden; +} +.popcorn-twitter-tweet-user { + font-weight: 700; +} +.popcorn-twitter-tweet-user > a { + font-weight: 400; + color: #28AAE1; +} +.popcorn-twitter-tweet-user > a:hover { + text-decoration: underline; +} +.popcorn-twitter-tweet-text { + font-size: 14px; +} + +/********************************************************* +* Ticker +*/ +.popcorn-twitter.ticker { + position: absolute; + top: auto !important; + left: 0 !important; + bottom: 0 !important; + height: 55px; + text-align: left; + color: #F1E79F; + font-size: 13px; + width: 100%; + filter: progid:DXImageTransform.Microsoft.Gradient(startColorstr='#222222', endColorstr='#1a1a1a', GradientType=0); + background: -webkit-gradient(linear, top, bottom, color-stop(0%, #222222), color-stop(100%, #1a1a1a)); + background: -webkit-linear-gradient(top, #222222 0%, #1a1a1a 100%); + background: -moz-linear-gradient(top, #222222 0%, #1a1a1a 100%); + background: -ms-linear-gradient(top, #222222 0%, #1a1a1a 100%); + background: -o-linear-gradient(top, #222222 0%, #1a1a1a 100%); + background: linear-gradient(top, #222222); + border: none; + /* Tweet */ + +} +.popcorn-twitter.ticker .popcorn-twitter-title { + display: none; +} +.popcorn-twitter.ticker .popcorn-twitter-tweets { + overflow: hidden; + margin-right: 10px; + background: url("twitter-icon.png") right 10px no-repeat; +} +.popcorn-twitter.ticker a { + text-decoration: none; + font-weight: 300; +} +.popcorn-twitter.ticker ul { + list-style: none; + padding: 0; + margin: 0; + margin-right: 10px; +} +.popcorn-twitter.ticker ul > li { + height: 55px; + border: none; + padding: 0; + padding-left: 10px; + -webkit-transition: margin-top 1s ease; + -moz-transition: margin-top 1s ease; + -o-transition: margin-top 1s ease; + -ms-transition: margin-top 1s ease; +} +.popcorn-twitter.ticker ul > li > div { + padding: 5px; +} +.popcorn-twitter.ticker .popcorn-twitter-tweet-image { + display: none; +} +.popcorn-twitter.ticker .popcorn-twitter-tweet-user { + font-weight: 700; +} +.popcorn-twitter.ticker .popcorn-twitter-tweet-user > a { + color: inherit; +} +.popcorn-twitter.ticker .popcorn-twitter-tweet-text { + font-size: 13px; +} +.popcorn-twitter.ticker:hover .popcorn-twitter-tweet-user > a { + color: #FFF; +} +.twitter-ticker-hidden { + display: none; +} \ No newline at end of file diff --git a/templates/assets/plugins/twitter/popcorn.twitter.html b/templates/assets/plugins/twitter/popcorn.twitter.html new file mode 100644 index 00000000..fb54c212 --- /dev/null +++ b/templates/assets/plugins/twitter/popcorn.twitter.html @@ -0,0 +1,58 @@ + + + + Popcorn Twitter Plug-in Demo + + + + + + + +

Popcorn Twitter Plug-in Demo

+

Allows for basic search queries against the twitter API. Allows for searches like Usernames(@), HashTags(#) or just generic ( "Kittens Are Awesome" )

+ +
+ +
+
+ + + diff --git a/templates/assets/plugins/twitter/popcorn.twitter.js b/templates/assets/plugins/twitter/popcorn.twitter.js new file mode 100644 index 00000000..b6372e0e --- /dev/null +++ b/templates/assets/plugins/twitter/popcorn.twitter.js @@ -0,0 +1,295 @@ +// PLUGIN: Twitter + +(function ( Popcorn, global ) { + + var CACHED_RESULTS = {}, + MAX_TWEETS = 150, + TWEETS_TIMER = 4000, + TRANSITION_MARGIN_TOP = "-55px", + TRANSITION_TIMEOUT = 700; + + Popcorn.plugin( "twitter", { + manifest: { + about: { + name: "Popcorn Maker Twitter Plugin", + version: "0.1", + author: "Matthew Schranz, @mjschranz", + website: "mschranz.wordpress.com, http://github.com/mjschranz" + }, + options: { + start: { + elem: "input", + type: "number", + label: "Start", + units: "seconds" + }, + end: { + elem: "input", + type: "number", + label: "End", + units: "seconds" + }, + search: { + elem: "input", + type: "text", + label: "Search", + "default": "Kittens", + optional: true + }, + username: { + elem: "input", + type: "text", + label: "Tweets from User", + optional: true + }, + searchType: { + elem: "select", + options: [ "Mixed", "Recent", "Popular" ], + values: [ "mixed", "recent", "popular" ], + label: "Search Results", + "default": "mixed", + "hidden": true + }, + numberOfTweets: { + elem: "input", + type: "number", + label: "Number of Tweets", + "default": 10, + optional: true, + maxTweets: MAX_TWEETS + }, + transition: { + elem: "select", + options: [ "None", "Pop", "Fade", "Slide Up", "Slide Down" ], + values: [ "popcorn-none", "popcorn-pop", "popcorn-fade", "popcorn-slide-up", "popcorn-slide-down" ], + label: "Transition", + "default": "popcorn-fade" + }, + layout: { + elem: "select", + options: [ "Ticker", "Feed" ], + values: [ "ticker", "feed" ], + label: "Tweet Layout", + "default": "feed", + optional: true + }, + left: { + hidden: true, + elem: "input", + type: "number", + units: "%", + "default": 0 + }, + zindex: { + hidden: true + } + } + }, + _setup: function( options ) { + var target = Popcorn.dom.find( options.target ), + requestString = "http://api.twitter.com/1/statuses/user_timeline.json?screen_name=", + titleText = document.createElement( "span" ), + outerTweetsContainer = document.createElement( "div" ), + tweetsContainer = document.createElement( "ul" ), + img, + tweetContainer, + imgLink, + tweetTextCont, + tweetUser, + tweetText, + allTweets = [], + query, + numberOfTweets = options.numberOfTweets; + + if ( !target ) { + target = this.media.parentNode; + } + + options._target = target; + + if ( !numberOfTweets ) { + numberOfTweets = options._natives.manifest.options.numberOfTweets[ "default" ]; + } else if ( numberOfTweets > MAX_TWEETS ) { + numberOfTweets = MAX_TWEETS; + } + + // safeguard against no search/username being provided + if ( !options.search && !options.username ) { + options.search = options._natives.manifest.options.search[ "default" ]; + } + + options._container = document.createElement( "div" ); + options._container.classList.add( "popcorn-twitter" ); + options._container.id = Popcorn.guid( "twitter" ); + options._container.style.left = options.left + "%"; + options._container.style.zIndex = +options.zindex; + titleText.classList.add( "popcorn-twitter-title" ); + titleText.appendChild( document.createTextNode( options.search || options.username || "Twitter" ) ); + + // Set layout class for container + if ( options.layout ) { + options._container.classList.add( options.layout ); + } + + // Set transitions for container + if ( options.transition ) { + options._container.classList.add( options.transition ); + options._container.classList.add( "off" ); + } + options._container.appendChild( titleText ); + + query = ( options.search || options.username ) + numberOfTweets; + + function buildTheTweets( tweets ) { + var currTweet, + twitterHandle, + twitterName, + imageLinkSource, + i, + len; + + // If we made it here, the query was a new one so store it in our cache + CACHED_RESULTS[ query ] = tweets; + + len = tweets.length; + + for ( i = 0; i < len; i++ ) { + currTweet = tweets[ i ]; + tweetContainer = document.createElement( "li" ); + img = document.createElement( "img" ); + imgLink = document.createElement( "a" ); + tweetTextCont = document.createElement( "div" ); + tweetUser = document.createElement( "div" ); + tweetUser.classList.add( "popcorn-twitter-tweet-user" ); + tweetText = document.createElement( "div" ); + tweetText.classList.add( "popcorn-twitter-tweet-text" ); + imageLinkSource = currTweet.profile_image_url || currTweet.user.profile_image_url; + twitterHandle = currTweet.from_user || currTweet.user.screen_name; + twitterName = currTweet.from_user_name || currTweet.user.name; + + imgLink.classList.add( "popcorn-twitter-tweet-image" ); + imgLink.href = img.src = imageLinkSource; + imgLink.target = "_blank"; // Ensure it opens in new tab/window + imgLink.appendChild( img ); + tweetContainer.appendChild( imgLink ); + + // Text Setup + tweetText.innerHTML = currTweet.text; + tweetUser.innerHTML = "" + + twitterName + " @" + twitterHandle; + tweetTextCont.appendChild( tweetUser ); + tweetTextCont.appendChild( tweetText ); + tweetContainer.appendChild( tweetTextCont ); + tweetsContainer.appendChild( tweetContainer ); + } + + // Set layout class for container + if ( options.layout ) { + options._container.classList.add( options.layout ); + if ( options.layout === "ticker" ) { + var elem; + + options._tickerInterval = setInterval(function() { + elem = tweetsContainer.firstChild; + elem.style.marginTop = TRANSITION_MARGIN_TOP; + setTimeout(function() { + tweetsContainer.removeChild( elem ); + tweetsContainer.appendChild( elem ); + elem.style.marginTop = ""; + }, TRANSITION_TIMEOUT ); + }, TWEETS_TIMER ); + } + } + + outerTweetsContainer.classList.add( "popcorn-twitter-tweets" ); + outerTweetsContainer.appendChild( tweetsContainer ); + options._container.appendChild( outerTweetsContainer ); + } + + function twitterCallback( e ) { + var results = e.results || e, + k, + rLen; + + for ( k = 0, rLen = results.length; k < rLen && allTweets.length < options.numberOfTweets; k++ ) { + allTweets.push( results[ k ] ); + } + + // Search API doesn't simply return count of tweets. It returns up to 100 + // and then provides a link to query the next "Page" of the same results + if ( allTweets.length < options.numberOfTweets && options.search ) { + Popcorn.xhr({ + url: requestString.substring( 0, requestString.indexOf( "?" ) ) + e.next_page, + dataType: "jsonp", + success: twitterCallback + }); + } else { + buildTheTweets( allTweets ); + } + } + + target.appendChild( options._container ); + + // We stored the results objects we get to save API calls being made + if ( !CACHED_RESULTS[ query ] ) { + if ( options.username ) { + Popcorn.xhr({ + url: "https://api.twitter.com/1/account/rate_limit_status.json", + dataType: "jsonp", + success: function( e ) { + if ( e.remaining_hits === 0 ) { + var warningText = document.createElement( "div" ); + + warningText.innerHTML = "You have hit the request limit for the hour. This will reset at " + + e.reset_time.substring( 0, e.reset_time.indexOf( "+" ) ) + " GMT."; + + options._container.appendChild( warningText ); + } else { + // Append various query options here + requestString += options.username + + "&count=" + options.numberOfTweets + "&include_rts=true"; + + Popcorn.xhr( { url: requestString, dataType: "jsonp", success: twitterCallback } ); + } + }}); + } else if ( options.search ) { + requestString = "http://search.twitter.com/search.json?q="; + + requestString += escape( options.search ) + + "&result_type=" + options.searchType; + + Popcorn.xhr( { url: requestString, dataType: "jsonp", success: twitterCallback } ); + + } + } else { + buildTheTweets( CACHED_RESULTS[ query ] ); + } + + options.toString = function() { + return options.username || options.search || options._natives.manifest.options.search[ "default" ]; + }; + }, + start: function( event, options ) { + if ( options._container ) { + options._container.classList.add( "on" ); + options._container.classList.remove( "off" ); + } + }, + end: function( event, options ) { + if ( options._container ) { + options._container.classList.add( "off" ); + options._container.classList.remove( "on" ); + } + }, + _teardown: function( options ) { + // Remove the plugins container when being destroyed + if ( options._container && options._target ) { + options._target.removeChild( options._container ); + } + + if ( options._tickerInterval ) { + clearInterval( options._tickerInterval ); + } + } + }); +}( Popcorn, this )); diff --git a/templates/assets/plugins/twitter/twitter-icon.png b/templates/assets/plugins/twitter/twitter-icon.png new file mode 100644 index 00000000..4034258d Binary files /dev/null and b/templates/assets/plugins/twitter/twitter-icon.png differ diff --git a/templates/assets/plugins/wikipedia/popcorn.wikipedia.js b/templates/assets/plugins/wikipedia/popcorn.wikipedia.js new file mode 100644 index 00000000..eb8374be --- /dev/null +++ b/templates/assets/plugins/wikipedia/popcorn.wikipedia.js @@ -0,0 +1,317 @@ +(function ( Popcorn ) { + + var allWikiLangLinks, allWikiLangNames; + + // shortcut + function create( type ) { + return document.createElement( type ); + } + + function getFragment( inputString ) { + //grabbed from butter util methods + var range = document.createRange(), + // For particularly speedy loads, 'body' might not exist yet, so try to use 'head' + container = document.body || document.head, + fragment; + + range.selectNode( container ); + fragment = range.createContextualFragment( inputString ); + + if( fragment.childNodes.length === 1 ){ + var child = fragment.firstChild; + fragment.removeChild( child ); + return child; + } + + return fragment; + } + + function validateDimension( value, fallback ) { + if ( typeof value === "number" ) { + return value; + } + return fallback; + } + + function sanitize( text ) { + return text.replace( /\(/g, "(" ) + .replace( /\)/g, ")" ) + .replace( /-/g, "‐" ) + .replace( /\s/g, " " ) + .replace( /,/g, "," ) + .replace( /'/g, "&apos" ); + } + + function areValidElements( element ) { + while( element && !element.textContent ){ + element = element.nextElementSibling; + if ( !element || element.nodeName !== "P" ) { + return false; + } + } + return true; + } + + var WikipediaDefinition = { + + _setup : function( options ) { + // declare needed variables + // get a guid to use for the global wikicallback function + var _title, + _titleDiv, + _titleTextArea, + _mainContentDiv, + _contentArea, + _toWikipedia, + _inner, + _outer, + _href, + _guid = Popcorn.guid( "wikiCallback" ); + + options._target = Popcorn.dom.find( options.target ); + + if ( !options._target ) { + return; + } + + options._container = _outer = create( "div" ); + _outer.classList.add( "wikipedia-outer-container" ); + + _outer.style.width = validateDimension( options.width, "100" ) + "%"; + _outer.style.height = validateDimension( options.height, "100" ) + "%"; + _outer.style.top = validateDimension( options.top, "0" ) + "%"; + _outer.style.left = validateDimension( options.left, "0" ) + "%"; + _outer.style.zIndex = +options.zindex; + + _inner = create( "div" ); + _inner.classList.add( "wikipedia-inner-container" ); + + _titleDiv = create( "div" ); + _titleDiv.classList.add( "wikipedia-title" ); + + _titleTextArea = create( "div" ); + _titleTextArea.classList.add( "wikipedia-title-text" ); + _titleTextArea.classList.add( "wikipedia-ellipsis" ); + + _titleDiv.appendChild( _titleTextArea ); + + _mainContentDiv = create( "div" ); + _mainContentDiv.classList.add( "wikipedia-main-content" ); + + _contentArea = create( "div" ); + _contentArea.classList.add( "wikipedia-content" ); + + _mainContentDiv.appendChild( _contentArea ); + + _toWikipedia = create( "a" ); + _toWikipedia.classList.add( "wikipedia-to-wiki" ); + + _inner.appendChild( _titleDiv ); + _inner.appendChild( _mainContentDiv ); + _inner.appendChild( _toWikipedia ); + + _outer.classList.add( options.transition ); + _outer.classList.add( "off" ); + + _outer.appendChild( _inner ); + options._target.appendChild( _outer ); + + if ( !options.lang ) { + options.lang = "en"; + } + + window[ _guid ] = function ( data ) { + + if ( data.error ) { + _titleTextArea.innerHTML = "Article Not Found"; + _contentArea.innerHTML = data.error.info; + return; + } + + var childIndex = 1, + responseFragment = getFragment( "
" + data.parse.text + "
" ), + element = responseFragment.querySelector( "div > p:nth-of-type(" + childIndex + ")" ), + mainText = ""; + + _titleTextArea.appendChild( getFragment( "" + sanitize( data.parse.title ) + "" ) ); + _toWikipedia.href = options._link; + _toWikipedia.setAttribute( "target", "_blank" ); + + while ( !areValidElements( element ) ) { + element = responseFragment.querySelector( "div > p:nth-of-type(" + ( ++childIndex ) + ")" ); + } + + while ( element && element.nodeName === "P" ) { + mainText += element.textContent + "
"; + element = element.nextElementSibling; + } + + _contentArea.innerHTML = mainText; + }; + + if ( options.src ) { + + _href = "//" + window.escape( options.lang ) + ".wikipedia.org/w/"; + _title = options.src.slice( options.src.lastIndexOf( "/" ) + 1 ); + options._link = "//" + window.escape( options.lang + ".wikipedia.org/wiki/" + _title ); + + // gets the mobile format, so that we don't load unwanted images when the respose is turned into a documentFragment + Popcorn.getScript( _href + "api.php?action=parse&prop=text&redirects&page=" + + window.escape( _title ) + "&noimages=1&mobileformat=html&format=json&callback=" + _guid ); + } + + options.toString = function() { + return options.src || options._natives.manifest.options.src[ "default" ]; + }; + }, + + start: function( event, options ){ + if ( options._container ) { + options._container.classList.add( "on" ); + options._container.classList.remove( "off" ); + } + }, + + end: function( event, options ){ + if ( options._container ) { + options._container.classList.add( "off" ); + options._container.classList.remove( "on" ); + } + }, + + _teardown: function( options ){ + if ( options._target && options._container ) { + options._target.removeChild( options._container ); + } + } + }; + + // Language codes: http://stats.wikimedia.org/EN/TablesDatabaseWikiLinks.htm + + allWikiLangLinks = ( "en,ja,es,de,ru,fr,it,pt,pl,zh,nl,tr,ar,sv,id,cs,fi,ko,th,fa,hu,he,no,vi,uk,da,ro" + + ",bg,hr,ca,el,sk,ms,sr,lt,sl,simple,eo,tl,et,hi,kk,sh,nn,ta,az,bs,af,eu,ka,lv,gl" + + ",zh_yue,tpi,mk,mr,la,ml,sq,be,cy,br,is,an,bn,war,oc,hy,arz,te,jv,ceb,sw" + + ",lb,als,ur,vo,fy,kn,gan,mg,ang,vec,gd,gu,ast,io,uz,qu,wuu,su,ku,yo,ga" + + ",tt,scn,bar,nds,se,ht,ne,ia,sco,lmo,mn,cv,ckb,diq,my,pnb,new,pms,zh-min-nan,yi,am" + + ",bpy,li,si,os,mt,nah,ps,fo,hsb,ilo,nap,wa,gv,ky,pam,sah,co,tg,ba,bcl" + + ",hif,km,sa,vls,or,mzn,ig,so,bo,kl,ksh,as,mi,szl,mwl,nrm,dsb,fiu-vro,dv,stq" + + ",tk,roa-rup,bug,mhr,kw,fur,sc,lad,csb,pa,rue,frr,gn,rm,ace,nv,bjn,arc,krc,ext,ug,nov" + + ",frp,crh,ab,lij,jbo,kv,ay,ce,ln,pdc,udm,eml,ie,mrj,xal,bh,hak,lo,wo" + + ",glk,myv,sn,chr,pag,rw,pcd,pap,zea,lbe,vep,koi,na,haw,cu,to,pi,av,zu,lez,kab,mdf," + + "tet,kaa,za,bm,rmy,kbd,iu,bi,kg,pih,ss,chy,ee,om,cr,cdo,srn,got,ha,bxr,ch,ty,sm,ltg," + + "pnt,ak,dz,st,sd,ik,ts,nso,y,tn,ki,ff,rn,xh,sg,ve,tw,ks,tum,fj,ti,lg" ).split( "," ); + + allWikiLangNames = ( "English,Japanese,Spanish,German,Russian,French,Italian,Portuguese,Polish," + + "Chinese,Dutch,Turkish,Arabic,Swedish,Indonesian,Czech,Finnish,Korean,Thai," + + "Persian,Hungarian,Hebrew,Norwegian,Vietnamese,Ukrainian,Danish,Romanian," + + "Bulgarian,Croatian,Catalan,Greek,Slovak,Malay,Serbian,Lithuanian,Slovene," + + "Simple English,Esperanto,Tagalog,Estonian,Hindi,Kazakh,Serbo-Croatian,Nynorsk," + + "Tamil,Azeri,Bosnian,Afrikaans,Basque,Georgian,Latvian,Galician,Cantonese," + + "Tok Pisin,Macedonian,Marathi,Latin,Malayalam,Albanian,Welsh,Breton," + + "Icelandic,Aragonese,Bengali,Waray-Waray,Occitan,Armenian,Egyptian Arabic," + + "Belarusian,Telugu,Javanese,Cebuano,Swahili,Luxembourgish,Alemannic,Urdu," + + "Volapuk,Frisian,Kannada,Gan,Malagasy,Anglo Saxon,Venetian," + + "Scots Gaelic,Gujarati,Asturian,Ido,Uzbek,Quechua,Wu,Sundanese,Kurdish,Yoruba," + + "Irish,Tatar,Sicilian,Bavarian,Low Saxon,Northern Sami,Haitian,Nepali," + + "Interlingua,Scots,Lombard,Mongolian,Chuvash,Sorani,Zazaki,Burmese,Western Panjabi" + + ",Nepal Bhasa,Piedmontese,Min Nan,Yiddish,Amharic,Bishnupriya Manipuri,Limburgish," + + "Sinhala,Ossetic,Maltese,Nahuatl,Pashto,Faroese,Upper Sorbian,Ilokano,Neapolitan," + + "Walloon,Manx,Kirghiz,Kapampangan,Sakha,Corsican,Tajik,Bashkir," + + "Central Bicolano,Fiji Hindi,Khmer,Sanskrit,West Flemish,Oriya,Mazandarani," + + "Igbo,Somali,Tibetan,Greenlandic,Ripuarian,Assamese,Maori,Silesian," + + "Mirandese,Norman,Lower Sorbian,Voro,Divehi,Saterland Frisian,Turkmen,Aromanian," + + "Buginese,Eastern Mari,Cornish,Friulian,Sardinian,Ladino,Cassubian,Punjabi,Rusyn," + + "North Frisian,Guarani,Romansh,Acehnese,Navajo,Banjar,Aramaic,Karachay-Balkar," + + "Extremaduran,Uyghur,Novial,Arpitan,Crimean Tatar,Abkhazian,Ligurian," + + "Lojban,Komi,Aymara,Chechen,Lingala,Pennsylvania German,Udmurt,Emilian-Romagnol," + + "Interlingue,Western Mari,Kalmyk,Bihari,Hakka,Laotian,Wolof,Gilaki," + + "Erzya,Shona,Cherokee,Pangasinan,Kinyarwanda,Picard,Papiamentu,Zealandic,Lak," + + "Vepsian,Komi Permyak,Nauruan,Hawai'ian,Old Church Slavonic,Tongan,Pali,Avar," + + "Zulu,Lezgian,Kabyle,Moksha,Tetum,Karakalpak,Zhuang,Bambara,Romani,Karbadian," + + "Inuktitut,Bislama,Kongo,Norfolk,Siswati,Cheyenne,Ewe,Oromo,Cree,Min Dong," + + "Sranan,Gothic,Hausa,Buryat,Chamorro,Tahitian,Samoan,Latgalian,Pontic,Akan," + + "Dzongkha,Sesotho,Sindhi,Inupiak,Tsonga,Northern Sotho,Chichewa,Setswana,Kikuyu," + + "Fulfulde,Kirundi,Xhosa,Sangro,Venda,Twi,Kashmiri,Tumbuka,Fijian,Tigrinya,Ganda" ).split( "," ); + + Popcorn.plugin( "wikipedia", WikipediaDefinition, { + about:{ + name: "Popcorn Wikipedia Plugin", + version: "0.1", + author: "@ChrisDeCairos", + website: "https://chrisdecairos.ca/" + }, + options:{ + start: { + elem: "input", + type: "number", + label: "Start", + "units": "seconds" + }, + end: { + elem: "input", + type: "number", + label: "End", + "units": "seconds" + }, + lang: { + elem: "select", + options: allWikiLangNames, + values: allWikiLangLinks, + label: "Language", + "default": "en" + }, + src: { + elem: "input", + type: "text", + label: "Article Link/Title", + "default": "Popcorn.js" + }, + width: { + elem: "input", + type: "number", + label: "Width", + "default": 40, + "units": "%", + "hidden": true + }, + height: { + elem: "input", + type: "number", + label: "Height", + "default": 50, + "units": "%", + "hidden": true + }, + top: { + elem: "input", + type: "number", + label: "Top", + "default": 25, + "units": "%", + "hidden": true + }, + left: { + elem: "input", + type: "number", + label: "Left", + "default": 30, + "units": "%", + "hidden": true + }, + target: { + hidden: true + }, + transition: { + elem: "select", + options: [ "None", "Pop", "Fade", "Slide Up", "Slide Down" ], + values: [ "popcorn-none", "popcorn-pop", "popcorn-fade", "popcorn-slide-up", "popcorn-slide-down" ], + label: "Transition", + "default": "popcorn-fade" + }, + zindex: { + hidden: true + } + } + }); + +}( Popcorn )); diff --git a/templates/assets/plugins/wikipedia/popcorn.wikipedia.less b/templates/assets/plugins/wikipedia/popcorn.wikipedia.less new file mode 100644 index 00000000..02de930f --- /dev/null +++ b/templates/assets/plugins/wikipedia/popcorn.wikipedia.less @@ -0,0 +1,151 @@ + +.wikipedia-outer-container { + @titleheight: 43px; + + position: absolute; + background: #FFF; + box-shadow: 0 4px 10px -5px rgba( 0, 0, 0, 0.2 ); + + .wikipedia-inner-container { + position: relative; + height: 100%; + width: 100%; + overflow: hidden; + } + .wikipedia-title { + height: @titleheight; + border-bottom: 2px solid #FFF; + line-height: @titleheight; + margin-bottom: 2px; + box-shadow: 0 4px 10px -5px #EEE, + inset 0 -1px #E6E6E6, + 0 1px #E6E6E6; + } + .wikipedia-title-text a { + display: block; + color: #666; + text-decoration: none; + font-size: 14px; + font-weight: 600; + border-left: 1px solid #E6E6E6; + padding-left: 10px; + overflow: hidden; + margin-right: 30px; + height: 100%; + text-overflow: ellipsis; + } + .wikipedia-title:before { + content: ""; + display: block; + background: url("wikipedia.png"); + height: 32px; + width: 32px; + float: left; + margin-top: 5px; + margin-left: 5px; + margin-right: 5px; + } + .wikipedia-main-content { + position: absolute; + top: 0; + bottom: 0.5em; + border-bottom: 1px solid #E6E6E6; + padding-left: 20px; + padding-top: @titleheight; + } + .wikipedia-content { + padding-top: 1em; + padding-bottom: 1.5em; + padding-right: 15px; + height: 100%; + overflow-y: scroll; + color: #000; + font-size: 11.5px; + line-height: 1.5; + font-family: georgia; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + } + .wikipedia-to-wiki { /* The arrow link */ + position: absolute; + top: 2px; + right: 5px; + overflow: hidden; + width: 30px; + height: 30px; + background: url( "wikipedia.png" ) 0 -25px no-repeat; + opacity: 0.3; + } + + .wikipedia-to-wiki:hover { + opacity: 1; + } +} + +._mediumMediaQueries() { + @titleheight: 55px; + .wikipedia-title { + height:@titleheight; + line-height:@titleheight; + } + .wikipedia-title a { + font-size: 18px; + font-weight: 400; + } + .wikipedia-title:before { + height: @titleheight; + width:@titleheight; + background-position: -32px 0; + } + .wikipedia-main-content { + padding-left: 40px; + padding-top: @titleheight; + } + .wikipedia-content { + font-size: 14px; + line-height: 1.8; + padding-right: 20px; + } +} + +._largeMediaQueries() { + .wikipedia-title a { + font-size: 20px; + font-weight: 400; + } + .wikipedia-main-content { + padding-left: 50px; + padding-top: 75px; + } + .wikipedia-content { + font-size: 18px; + line-height: 1.8; + padding-right: 40px; + } +} + + +@media only screen and (min-width: 853px) and (min-height : 480px) { + ._mediumMediaQueries(); +} + +@media only screen and (min-width: 1280px) and (min-height : 720px) { + ._largeMediaQueries(); +} + +/********************************************************* +* Butter-specific. Ugh.... +*/ + +@media only screen and (min-width : 1203px) and (min-height : 780px) { + .butter-header-spacing { + ._mediumMediaQueries() + } +} + +@media only screen and (min-width : 1630px) and (min-height : 1020px) { + .butter-header-spacing { + ._largeMediaQueries() + } +} diff --git a/templates/assets/plugins/wikipedia/wikipedia-icon.png b/templates/assets/plugins/wikipedia/wikipedia-icon.png new file mode 100644 index 00000000..7a4814ff Binary files /dev/null and b/templates/assets/plugins/wikipedia/wikipedia-icon.png differ diff --git a/templates/assets/plugins/wikipedia/wikipedia.png b/templates/assets/plugins/wikipedia/wikipedia.png new file mode 100644 index 00000000..cc0c0b30 Binary files /dev/null and b/templates/assets/plugins/wikipedia/wikipedia.png differ diff --git a/templates/basic/config.json b/templates/basic/config.json new file mode 100644 index 00000000..877e4733 --- /dev/null +++ b/templates/basic/config.json @@ -0,0 +1,14 @@ +{ + "name": "basic", + "template": "index.html", + "baseDir": "../../", + "savedDataUrl": "saved-data.json", + "editor": { + "googlemap": "{{baseDir}}editors/googlemap-editor.js", + "popup": "{{baseDir}}templates/assets/editors/popup/popup-editor.js", + "image": "{{baseDir}}templates/assets/editors/image/image-editor.js", + "text": "{{baseDir}}templates/assets/editors/text/text-editor.js", + "twitter": "{{baseDir}}templates/assets/editors/twitter/twitter-editor.js" + }, + "maxPluginZIndex": 1000 +} diff --git a/templates/basic/index.html b/templates/basic/index.html new file mode 100644 index 00000000..46093860 --- /dev/null +++ b/templates/basic/index.html @@ -0,0 +1,48 @@ + + + + + + + Popcorn Maker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ +
+
+ + diff --git a/templates/basic/lego.json b/templates/basic/lego.json new file mode 100644 index 00000000..b80f5a5d --- /dev/null +++ b/templates/basic/lego.json @@ -0,0 +1,341 @@ +{ + "targets": [{ + "id": "Target0", + "name": "video-container", + "element": "video-container" + }], + "media": [{ + "id": "Media0", + "name": "Media0", + "url": ["http://videos-origin.mozilla.org/serv/webmademovies/legorough02.webm?butteruid=1347050894047", "http://videos-origin.mozilla.org/serv/webmademovies/legorough02.mp4?butteruid=1347050894048"], + "target": "video", + "duration": 78.908, + "controls": true, + "tracks": [{ + "name": "Layer0", + "id": "0", + "trackEvents": [{ + "id": "TrackEvent1", + "type": "text", + "popcornOptions": { + "start": 0, + "end": 3.44305, + "target": "video-container", + "text": "Molly Presents", + "zindex": 1000, + "fontColor": "#ffffff", + "position": "center", + "fontSize": "120" + }, + "track": "Layer0", + "name": "TrackEvent3" + }, { + "id": "TrackEvent3", + "type": "text", + "popcornOptions": { + "start": 3.66883, + "end": 5.95479, + "target": "video-container", + "text": "The Lego Madlib Adventure", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Merriweather", + "fontSize": "100", + "fontColor": "#ffffff", + "fontWeight": true, + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent2" + }, { + "id": "TrackEvent5", + "type": "text", + "popcornOptions": { + "start": 8.35364, + "end": 9.22851, + "target": "video-container", + "text": "Molly!", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Fredoka One", + "fontSize": "100", + "fontColor": "#000", + "fontWeight": "", + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent3" + }, { + "id": "TrackEvent7", + "type": "googlemap", + "popcornOptions": { + "start": 11.11736, + "end": 13.92197, + "target": "video-container", + "type": "ROADMAP", + "location": "", + "fullscreen": "", + "heading": 0, + "pitch": 1, + "zoom": 13, + "transition": "popcorn-fade", + "left": 5, + "top": 5, + "width": 34.81828839390387, + "height": 71.90496222956673, + "lat": 48.37139427955219, + "lng": -123.73325814282224, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent4" + }, { + "id": "TrackEvent9", + "type": "text", + "popcornOptions": { + "start": 15.86411, + "end": 17.58928, + "target": "video-container", + "text": "Magic", + "position": "custom", + "transition": "popcorn-fade", + "fontFamily": "Merriweather", + "fontSize": 48, + "fontColor": "#ccc000", + "fontWeight": "", + "fontItalics": "", + "textUnderline": "", + "left": 47.8194635889874, + "top": 9.573433830599832, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent5" + }, { + "id": "TrackEvent11", + "type": "wikipedia", + "popcornOptions": { + "start": 21.52718, + "end": 25.84012, + "target": "video-container", + "lang": "en", + "src": "http://en.wikipedia.org/wiki/Boredom", + "width": 50.9375, + "height": 87.77777777777777, + "top": 2.7777777777777777, + "left": 2.1875, + "transition": "popcorn-fade", + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent10" + }, { + "id": "TrackEvent13", + "type": "text", + "popcornOptions": { + "start": 44.51221, + "end": 45.84833, + "target": "video-container", + "text": "Dude", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Merriweather", + "fontSize": 48, + "fontColor": "#fff", + "fontWeight": "", + "fontItalics": true, + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent14" + }, { + "id": "TrackEvent15", + "type": "wikipedia", + "popcornOptions": { + "start": 53.48336, + "end": 59.01876, + "target": "video-container", + "lang": "en", + "src": "http://en.wikipedia.org/wiki/Mushroom", + "width": 40.3125, + "height": 84.44444444444444, + "top": 4.444444444444445, + "left": 0.3125, + "transition": "popcorn-fade", + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent15" + }, { + "id": "TrackEvent21", + "type": "text", + "popcornOptions": { + "start": 35.07671, + "end": 36.18562, + "target": "video-container", + "text": "Queen awesomesauce", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Bangers", + "fontSize": "36", + "fontColor": "#000", + "fontWeight": false, + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent12" + }, { + "id": "TrackEvent23", + "type": "text", + "popcornOptions": { + "start": 41.95447, + "end": 42.90885, + "target": "video-container", + "text": "The bad guy", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Vollkorn", + "fontSize": 48, + "fontColor": "#000000", + "fontWeight": "", + "fontItalics": true, + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent13" + }, { + "id": "TrackEvent28", + "type": "text", + "popcornOptions": { + "start": 73.29626, + "end": 76.57932, + "target": "video-container", + "text": "Change the timeline in popcorn!", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Bangers", + "fontSize": "80", + "fontColor": "#ffffff", + "fontWeight": "", + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent17" + }, { + "id": "TrackEvent29", + "type": "text", + "popcornOptions": { + "start": 68.21896, + "end": 73.33443, + "target": "video-container", + "text": "Make your own story!", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Bangers", + "fontSize": "120", + "fontColor": "#ffffff", + "fontWeight": false, + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent16" + }, { + "id": "TrackEvent33", + "type": "text", + "popcornOptions": { + "start": 76.80837, + "end": 78.64077, + "target": "video-container", + "text": "Music: \"Harvestfred\" - Primavara - CC-BY", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Bangers", + "fontSize": 48, + "fontColor": "#ffffff", + "fontWeight": "", + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent30" + }] + }, { + "name": "Layer1", + "id": "1", + "trackEvents": [{ + "id": "TrackEvent25", + "type": "text", + "popcornOptions": { + "start": 11.14625, + "end": 13.75247, + "target": "video-container", + "text": "Sooke", + "position": "bottom", + "transition": "popcorn-fade", + "fontFamily": "Merriweather", + "fontSize": 48, + "fontColor": "#000", + "fontWeight": "", + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 999 + }, + "track": "Layer1", + "name": "TrackEvent18" + }, { + "id": "TrackEvent27", + "type": "text", + "popcornOptions": { + "start": 28.09035, + "end": 30.37808, + "target": "video-container", + "text": "Señor jerkpants", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "PT Sans", + "fontSize": "42", + "fontColor": "#000", + "fontWeight": "", + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 999 + }, + "track": "Layer1", + "name": "TrackEvent11" + }] + }] + }] +} \ No newline at end of file diff --git a/templates/basic/meme.json b/templates/basic/meme.json new file mode 100644 index 00000000..12f82fec --- /dev/null +++ b/templates/basic/meme.json @@ -0,0 +1,101 @@ +{ + "targets": [{ + "id": "Target0", + "name": "video-container", + "element": "video-container" + }], + "media": [{ + "id": "Media0", + "name": "Media0", + "url": ["https://www.youtube.com/watch?v=Qw9oX-kZ_9k&NR=1&feature=fvwp&butteruid=1347052070809"], + "target": "video", + "duration": 6.706, + "controls": true, + "tracks": [{ + "name": "Layer2", + "id": "1", + "trackEvents": [{ + "id": "TrackEvent3", + "type": "text", + "popcornOptions": { + "start": 0, + "end": 0.54223, + "target": "video-container", + "text": "WHAT", + "position": "custom", + "transition": "popcorn-fade", + "fontFamily": "Open Sans", + "fontSize": "24", + "fontColor": "#ffffff", + "fontWeight": true, + "fontItalics": "", + "textUnderline": "", + "left": 63.411488147301796, + "top": 15.409202061354963, + "zindex": 999 + }, + "track": "Layer2", + "name": "TrackEvent13" + }, { + "id": "TrackEvent5", + "type": "text", + "popcornOptions": { + "start": 0.68126, + "end": 1.16324, + "target": "video-container", + "text": "wat", + "position": "left", + "transition": "popcorn-fade", + "fontFamily": "Merriweather", + "fontSize": 48, + "fontColor": "#fff", + "fontWeight": true, + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 999 + }, + "track": "Layer2", + "name": "TrackEvent4" + }, { + "id": "TrackEvent7", + "type": "text", + "popcornOptions": { + "start": 1.57816, + "end": 2.01468, + "target": "video-container", + "text": "wut", + "position": "right", + "transition": "popcorn-fade", + "fontFamily": "Merriweather", + "fontSize": 48, + "fontColor": "#ffffff", + "fontWeight": "", + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 999 + }, + "track": "Layer2", + "name": "TrackEvent5" + }] + }, { + "name": "Layer3", + "id": "2", + "trackEvents": [{ + "id": "TrackEvent8", + "type": "loopPlugin", + "popcornOptions": { + "start": 0, + "end": 2.71333, + "target": "video-container", + "loop": 0 + }, + "track": "Layer3", + "name": "TrackEvent8" + }] + }] + }] +} \ No newline at end of file diff --git a/templates/basic/saved-data.json b/templates/basic/saved-data.json new file mode 100644 index 00000000..fe0b1240 --- /dev/null +++ b/templates/basic/saved-data.json @@ -0,0 +1,56 @@ +{ + "targets": [ + { + "id": "video-container", + "name": "video-container", + "element": "video-container" + } + ], + "media": [ + { + "id": "Media0", + "name": "Media0", + "url": [ + "http://videos-cdn.mozilla.net/serv/webmademovies/makecorn.mp4", + "http://videos-cdn.mozilla.net/serv/webmademovies/makecorn.ogv", + "http://videos-cdn.mozilla.net/serv/webmademovies/makecorn.webm" + ], + "target": "video", + "duration": 64.54166412353516, + "tracks": [ + { + "name": "Layer", + "id": "Layer0", + "trackEvents": [ + { + "id": "TrackEvent3", + "type": "text", + "popcornOptions": { + "start": 1, + "end": 2.8, + "target": "video-container", + "text": "Pop some corn!" + }, + "track": "Track0", + "name": "TrackEvent3" + }, + { + "id": "TrackEvent3", + "type": "text", + "popcornOptions": { + "start": 3, + "end": 5, + "target": "video-container", + "text": "Pop some corn!", + "top": "20", + "left": "20" + }, + "track": "Track0", + "name": "TrackEvent3" + } + ] + } + ] + } + ] +} diff --git a/templates/basic/soundscape.json b/templates/basic/soundscape.json new file mode 100644 index 00000000..a58d52d9 --- /dev/null +++ b/templates/basic/soundscape.json @@ -0,0 +1,276 @@ +{ + "targets": [{ + "id": "Target0", + "name": "video-container", + "element": "video-container" + }], + "media": [{ + "id": "Media0", + "name": "Media0", + "url": ["http://soundcloud.com/brettgaylor/chinatown-tour?butteruid=1346972768767"], + "target": "video", + "duration": 128.299, + "controls": true, + "tracks": [{ + "name": "Layer1", + "id": "0", + "trackEvents": [{ + "id": "TrackEvent1", + "type": "image", + "popcornOptions": { + "start": 0, + "end": 8.94789, + "target": "video-container", + "src": "http://farm5.staticflickr.com/4097/4822261716_54e0cc3338_b.jpg", + "tags": "", + "photosetId": "", + "count": "", + "width": 80, + "height": 80, + "top": 10, + "left": 10, + "transition": "popcorn-fade", + "zindex": 1000 + }, + "track": "Layer1", + "name": "TrackEvent2" + }, { + "id": "TrackEvent3", + "type": "image", + "popcornOptions": { + "start": 13.35301, + "end": 18.58408, + "target": "video-container", + "src": "http://farm2.staticflickr.com/1071/1200161954_4d4a4df8b9_b.jpg", + "tags": "", + "photosetId": "", + "count": "", + "width": 80, + "height": 80, + "top": 20.008337316053353, + "left": 20.046893317702228, + "transition": "popcorn-fade", + "zindex": 1000 + }, + "track": "Layer1", + "name": "TrackEvent5" + }, { + "id": "TrackEvent7", + "type": "wikipedia", + "popcornOptions": { + "start": 31.8912, + "end": 45.65719, + "target": "video-container", + "lang": "en", + "src": "http://en.wikipedia.org/wiki/History_of_Chinese_immigration_to_Canada", + "width": 97.42086752637749, + "height": 70.61275456704664, + "top": 29.585243968262226, + "left": 0.45721270973769046, + "transition": "popcorn-fade", + "zindex": 1000 + }, + "track": "Layer1", + "name": "TrackEvent8" + }, { + "id": "TrackEvent9", + "type": "googlemap", + "popcornOptions": { + "start": 123.299, + "end": 128.299, + "target": "video-container", + "type": "STREETVIEW", + "location": "pandora street and government victoria bc", + "fullscreen": true, + "heading": 0, + "pitch": 1, + "zoom": 10, + "transition": "popcorn-fade", + "left": 0, + "top": 0, + "width": 100, + "height": 100, + "lat": 0, + "lng": 0, + "zindex": 1000 + }, + "track": "Layer1", + "name": "TrackEvent22" + }] + }, { + "name": "Layer2", + "id": "1", + "trackEvents": [{ + "id": "TrackEvent11", + "type": "image", + "popcornOptions": { + "start": 9.13144, + "end": 13.35301, + "target": "video-container", + "src": "http://farm3.staticflickr.com/2629/3850719023_d7b222e47e_b.jpg", + "tags": "", + "photosetId": "", + "count": "", + "width": 80, + "height": 80, + "top": 20.008337316053353, + "left": 0, + "transition": "popcorn-fade", + "zindex": 999 + }, + "track": "Layer2", + "name": "TrackEvent3" + }, { + "id": "TrackEvent13", + "type": "image", + "popcornOptions": { + "start": 18.72174, + "end": 26.79779, + "target": "video-container", + "src": "http://farm4.staticflickr.com/3448/3851511272_b9617e06a9_b.jpg", + "tags": "", + "photosetId": "", + "count": "", + "width": 80, + "height": 80, + "top": 0, + "left": 20.046893317702228, + "transition": "popcorn-fade", + "zindex": 999 + }, + "track": "Layer2", + "name": "TrackEvent6" + }, { + "id": "TrackEvent15", + "type": "googlemap", + "popcornOptions": { + "start": 46.07017, + "end": 65.34255, + "target": "video-container", + "type": "STREETVIEW", + "location": "fan tan alley", + "fullscreen": true, + "heading": 0, + "pitch": 1, + "zoom": "1", + "transition": "popcorn-fade", + "left": 0, + "top": 0, + "width": 100, + "height": 100, + "lat": 48.429188302782684, + "lng": -123.36828567553255, + "zindex": 999 + }, + "track": "Layer2", + "name": "TrackEvent10" + }, { + "id": "TrackEvent17", + "type": "image", + "popcornOptions": { + "start": 65.75553, + "end": 71.62902, + "target": "video-container", + "src": "http://farm5.staticflickr.com/4014/4534030270_54e52cce54_b.jpg", + "tags": "", + "photosetId": "", + "count": "", + "width": 44.4314185228605, + "height": 80.03334926421341, + "top": 14.578990981222212, + "left": 24.417740351150353, + "transition": "popcorn-fade", + "zindex": 999 + }, + "track": "Layer2", + "name": "TrackEvent11" + }, { + "id": "TrackEvent19", + "type": "image", + "popcornOptions": { + "start": 82.59592, + "end": 88.28586, + "target": "video-container", + "src": "http://farm8.staticflickr.com/7193/6836329278_6c98168e97_b.jpg", + "tags": "", + "photosetId": "", + "count": "", + "width": 49.0625, + "height": 79.16666666666666, + "top": 10, + "left": 10, + "transition": "popcorn-fade", + "zindex": 999 + }, + "track": "Layer2", + "name": "TrackEvent18" + }, { + "id": "TrackEvent21", + "type": "image", + "popcornOptions": { + "start": 77.06658, + "end": 82.06658, + "target": "video-container", + "src": "http://farm8.staticflickr.com/7166/6549277201_594aa4ba1a_b.jpg", + "tags": "", + "photosetId": "", + "count": "", + "width": 42.96875, + "height": 90, + "top": 10, + "left": 10, + "transition": "popcorn-fade", + "zindex": 999 + }, + "track": "Layer2", + "name": "TrackEvent19" + }, { + "id": "TrackEvent23", + "type": "wikipedia", + "popcornOptions": { + "start": 98.88567, + "end": 123.1597, + "target": "video-container", + "lang": "en", + "src": "http://en.wikipedia.org/wiki/Fan_Tan_Alley", + "width": 100, + "height": 70.55555555555556, + "top": 29.444444444444446, + "left": 0, + "transition": "popcorn-fade", + "zindex": 999 + }, + "track": "Layer2", + "name": "TrackEvent21" + }] + }, { + "name": "Layer2", + "id": "2", + "trackEvents": [{ + "id": "TrackEvent25", + "type": "googlemap", + "popcornOptions": { + "start": 71.85845, + "end": 77.08953, + "target": "video-container", + "type": "STAMEN-TERRAIN", + "location": "", + "fullscreen": true, + "heading": 0, + "pitch": 1, + "zoom": "19", + "transition": "popcorn-fade", + "left": 0, + "top": 0, + "width": 100, + "height": 100, + "lat": 48.42885717902951, + "lng": -123.36792541534425, + "zindex": 998 + }, + "track": "Layer2", + "name": "TrackEvent16" + }] + }] + }] +} \ No newline at end of file diff --git a/templates/basic/style.css b/templates/basic/style.css new file mode 100755 index 00000000..482b343e --- /dev/null +++ b/templates/basic/style.css @@ -0,0 +1,119 @@ +/* ===== Primary Styles ======================================================== + Author: @k88hudson + ========================================================================== */ + +.wrapper { + width: 560px; + border: 15px solid #FFF; + margin: 40px auto; + box-shadow: 0 0 0 1px #CCC, + 0 25px 5px -20px rgba( 0, 0, 0, 0.5 ); +} + +/* body-wrapper required to circumvent this bug in webkit: https://bugs.webkit.org/show_bug.cgi?id=77811 */ +.butter-header-spacing .body-wrapper { + position: absolute; + left: 0; + top: 80px; /* compensate for header */ + right: 350px; + bottom: 0; + -webkit-transition: right 0.25s; + -moz-transition: right 0.25s; + -ms-transition: right 0.25s; + -o-transition: right 0.25s; + transition: right 0.25s; +} + +body.editor-minimized .body-wrapper { + right: 0; +} + +/* VIDEO +-------------------------------------------------- */ +.video-container { + position: relative; +} + +.video { + position: relative; + width: 100%; + padding-bottom: 56.25%; + height: 0; +} + +.video > video, +.video > iframe, +.video > object { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.video > iframe { + z-index: -1; +} + +.video > video { + background: #000; +} + +/* Use this class for a full overlay */ +.video-overlay { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + overflow: hidden; +} + +/* Sidebar Layout +-------------------------------------------------- */ + +.wrapper-sidebar .video-container, +.wrapper-sidebar .sidebar { + float: left; +} +.wrapper-sidebar .video-container { + width: 75%; +} +.wrapper-sidebar .sidebar { + display: block; + position: relative; + width: 25%; + padding-bottom: 42.1875%; + color: #222; + height: 0; +} +.wrapper-sidebar .sidebar > .sidebar-inner { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #FFF; + overflow: scroll; +} + + +/* MEDIA QUERIES +-------------------------------------------------- */ +/* +Magic numbers: +350 is the horizontal offset +300 is the vertical offset (tray + header) +*/ + +@media only screen and (min-width : 990px) and (min-height : 660px) { + .wrapper { width: 640px; } +} + +@media only screen and (min-width : 1203px) and (min-height : 780px) { + .wrapper { width: 853px; } +} + +@media only screen and (min-width : 1630px) and (min-height : 1020px) { + .wrapper { width: 1280px; } +} diff --git a/templates/basic/template.js b/templates/basic/template.js new file mode 100644 index 00000000..b3e89f59 --- /dev/null +++ b/templates/basic/template.js @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the MIT license + * If a copy of the MIT license was not distributed with this file, you can + * obtain one at http://www.mozillapopcorn.org/butter-license.txt */ + +( function( Butter, EditorHelper ) { + document.addEventListener( "DOMContentLoaded", function() { + Butter.init({ + config: "config.json", + ready: function( butter ) { + var script; + EditorHelper.init( butter ); + script = document.createElement( "script" ); + script.src = "//www.mozilla.org/tabzilla/media/js/tabzilla.js"; + document.body.appendChild( script ); + } + }); + }, false ); +}( window.Butter, window.EditorHelper ) ); diff --git a/templates/basic/tour.json b/templates/basic/tour.json new file mode 100644 index 00000000..33e394c1 --- /dev/null +++ b/templates/basic/tour.json @@ -0,0 +1,186 @@ +{ + "targets": [{ + "id": "Target0", + "name": "video-container", + "element": "video-container" + }], + "media": [{ + "id": "Media0", + "name": "Media0", + "url": ["http://videos-origin.mozilla.org/serv/webmademovies/neighbourhood2.webm?butteruid=1346973383492", "http://videos-origin.mozilla.org/serv/webmademovies/neighbourhood2.mp4?butteruid=1346973383493"], + "target": "video", + "duration": 31.949, + "controls": true, + "tracks": [{ + "name": "Layer0", + "id": "0", + "trackEvents": [{ + "id": "TrackEvent1", + "type": "text", + "popcornOptions": { + "start": 0, + "end": 1.98768, + "target": "video-container", + "text": "Three things about my neighbourhood", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Merriweather", + "fontSize": 48, + "fontColor": "#fff000", + "fontWeight": "", + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent3" + }, { + "id": "TrackEvent3", + "type": "text", + "popcornOptions": { + "start": 2.07537, + "end": 4.42843, + "target": "video-container", + "text": "By Orville Redenbacher", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Merriweather", + "fontSize": 48, + "fontColor": "#FFF000", + "fontWeight": "", + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent3" + }, { + "id": "TrackEvent5", + "type": "wikipedia", + "popcornOptions": { + "start": 5.48073, + "end": 7.9361, + "target": "video-container", + "lang": "en", + "src": "http://en.wikipedia.org/wiki/Fernwood,_Greater_Victoria", + "width": 40, + "height": 50, + "top": 2.282200339064422, + "left": 59.191089554101325, + "transition": "popcorn-fade", + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent5" + }, { + "id": "TrackEvent7", + "type": "wikipedia", + "popcornOptions": { + "start": 9.58762, + "end": 12.42299, + "target": "video-container", + "lang": "en", + "src": "http://en.wikipedia.org/wiki/Belfry_Theatre", + "width": 40, + "height": 96.66666666666667, + "top": 0.8333333333333334, + "left": 60, + "transition": "popcorn-fade", + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent6" + }, { + "id": "TrackEvent9", + "type": "googlemap", + "popcornOptions": { + "start": 14.0599, + "end": 20.09601, + "target": "video-container", + "type": "ROADMAP", + "location": "gladstone and fernwood victoria", + "fullscreen": "", + "heading": 0, + "pitch": 1, + "zoom": 17, + "transition": "popcorn-fade", + "left": 5, + "top": 5, + "width": 41.090269636576785, + "height": 66.59024762999006, + "lat": 0, + "lng": 0, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent8" + }, { + "id": "TrackEvent11", + "type": "wikipedia", + "popcornOptions": { + "start": 21.98138, + "end": 26.27827, + "target": "video-container", + "lang": "en", + "src": "http://en.wikipedia.org/wiki/Permaculture", + "width": 40, + "height": 50, + "top": 45.00833731605336, + "left": 59.07385625984577, + "transition": "popcorn-fade", + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent9" + }, { + "id": "TrackEvent13", + "type": "text", + "popcornOptions": { + "start": 27.72518, + "end": 29.11364, + "target": "video-container", + "text": "Tell us about your neighbourhood!", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Merriweather", + "fontSize": 48, + "fontColor": "#FFF", + "fontWeight": "", + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent10" + }, { + "id": "TrackEvent15", + "type": "text", + "popcornOptions": { + "start": 29.19322, + "end": 30.19322, + "target": "video-container", + "text": "Remix this video!", + "position": "center", + "transition": "popcorn-fade", + "fontFamily": "Merriweather", + "fontSize": 48, + "fontColor": "#FFF", + "fontWeight": "", + "fontItalics": "", + "textUnderline": "", + "left": 30, + "top": 30, + "zindex": 1000 + }, + "track": "Layer0", + "name": "TrackEvent11" + }] + }] + }] +} \ No newline at end of file diff --git a/templates/basic/tutorial.json b/templates/basic/tutorial.json new file mode 100644 index 00000000..982d17d7 --- /dev/null +++ b/templates/basic/tutorial.json @@ -0,0 +1,126 @@ +{ + "targets": [ + { + "id": "video-container", + "name": "video-container", + "element": "video-container" + } + ], + "media": [ + { + "id": "Media0", + "name": "Media0", + "url": [ + "http://videos-cdn.mozilla.net/serv/webmademovies/makecorn.mp4", + "http://videos-cdn.mozilla.net/serv/webmademovies/makecorn.ogv", + "http://videos-cdn.mozilla.net/serv/webmademovies/makecorn.webm" + ], + "target": "video", + "duration": 64.54166412353516, + "tracks": [ + { + "name": "Layer", + "id": "Layer0", + "trackEvents": [ + { + "id": "TrackEvent3", + "type": "text", + "popcornOptions": { + "start": 1, + "end": 6, + "target": "video-container", + "text": "Pop some corn!" + }, + "track": "Track0", + "name": "TrackEvent3" + } + ] + } + ] + } + ], + "tutorial": { + "general": [ + { + "start": 1, + "end": 6, + "name": "media-editor-button-tooltip", + "element": ".butter-editor-header-media", + "message": "This is The Media Editor", + "top": "50px", + "left": "50px", + "hover": false, + "hidden": true + }, + { + "start": 1, + "end": 5, + "name": "save-button-tooltip", + "element": ".butter-save-btn", + "message": "Press to save your project", + "top": "45px", + "left": "50%", + "hover": false, + "hidden": true + } + ], + "plugin-list": [ + { + "start": 1, + "end": 6, + "name": "image-plugin-button-tooltip", + "element": ".butter-plugin-tile[data-popcorn-plugin-type=image]", + "message": "Drag me to the timeline!", + "top": "50px", + "left": "50px", + "hover": false, + "hidden": true + }, + { + "start": 15, + "end": 20, + "name": "skip-plugin-button-tooltip", + "element": ".butter-plugin-tile[data-popcorn-plugin-type=skip]", + "message": "I make the video skip forward!", + "top": "50px", + "left": "50px", + "hover": false, + "hidden": true + } + ], + "media-editor": [ + { + "start": 8, + "end": 14, + "name": "new-media-button-tooltip", + "element": "#new-media-source-btn", + "message": "click me to add a new media source!", + "top": "50px", + "left": "50px", + "hover": false, + "hidden": true + }, + { + "start": 10, + "end": 14, + "name": "alternate-media-button-tooltip", + "element": "#add-alternate-media-source-btn", + "message": "click me to add a fallback media!", + "top": "50px", + "left": "50px", + "hover": false, + "hidden": true + } + ], + "editorOpenEvents": [ + { + "start": 7, + "type": "media-editor" + }, + { + "start": 14, + "type": "plugin-list" + } + ] + } +} diff --git a/test/beautifier/array.expected.js b/test/beautifier/array.expected.js new file mode 100644 index 00000000..ed3b5503 --- /dev/null +++ b/test/beautifier/array.expected.js @@ -0,0 +1,4 @@ +[ "a", "soclose", "gimmie some space" ] +inAFunction( "ASD", [ "yeap" ], "ASDASD'ASDASD'ASDASD" ); +[ "fixmeplease", prettyPlease ]; +[ callingFuncs(), yeap( with, some, argssssss ) ]; diff --git a/test/beautifier/array.js b/test/beautifier/array.js new file mode 100644 index 00000000..214d95a8 --- /dev/null +++ b/test/beautifier/array.js @@ -0,0 +1,4 @@ +["a","soclose","gimmie some space"] +inAFunction( "ASD", [ "yeap" ] , "ASDASD'ASDASD'ASDASD" ) ; +[ "fixmeplease", prettyPlease ]; +[ callingFuncs() , yeap( with, some, argssssss )]; diff --git a/test/beautifier/comments.expected.js b/test/beautifier/comments.expected.js new file mode 100644 index 00000000..a16beb0e --- /dev/null +++ b/test/beautifier/comments.expected.js @@ -0,0 +1,24 @@ +// single line comment +if ( this ) { + that(); +} + +/* multi + * line + * comment + */ +if ( multi() ) { + doMulti(); +} + +function nester() { + // nested single line + var single = 0; + + /* nested + * double + * line + * comment + */ + isFine = true; +} diff --git a/test/beautifier/comments.js b/test/beautifier/comments.js new file mode 100644 index 00000000..32eef9d0 --- /dev/null +++ b/test/beautifier/comments.js @@ -0,0 +1,25 @@ +// single line comment + +if ( this ) { + that(); +} + +/* multi + * line + * comment + */ +if ( multi() ) { + doMulti(); +} + +function nester() { + // nested single line + var single = 0; + + /* nested + * double + * line + * comment + */ + isFine = true; +} diff --git a/test/beautifier/eolspace.expected.js b/test/beautifier/eolspace.expected.js new file mode 100644 index 00000000..ef4ba19c --- /dev/null +++ b/test/beautifier/eolspace.expected.js @@ -0,0 +1,5 @@ +var a = 0; + +if ( this ) { + doSomething(); +} diff --git a/test/beautifier/eolspace.js b/test/beautifier/eolspace.js new file mode 100644 index 00000000..b2f3dc2b --- /dev/null +++ b/test/beautifier/eolspace.js @@ -0,0 +1,5 @@ +var a = 0; + +if ( this ) { + doSomething(); +} diff --git a/test/beautifier/for.expected.js b/test/beautifier/for.expected.js new file mode 100644 index 00000000..6dbe20ba --- /dev/null +++ b/test/beautifier/for.expected.js @@ -0,0 +1,4 @@ +for ( var i = 0; j < 10; j++) { + unCrunch(); +} +for ( i = 0; i < 1000; i++) {} diff --git a/test/beautifier/for.js b/test/beautifier/for.js new file mode 100644 index 00000000..c99ff8ce --- /dev/null +++ b/test/beautifier/for.js @@ -0,0 +1,2 @@ +for(var i=0; j<10; j++){unCrunch();} +for ( i = 0; i < 1000; i++ ) {} diff --git a/test/beautifier/function.expected.js b/test/beautifier/function.expected.js new file mode 100644 index 00000000..886c994f --- /dev/null +++ b/test/beautifier/function.expected.js @@ -0,0 +1,7 @@ +function spaces() { + fixIt(); +} +var func = function() { + fixThisToo(); + }; +callMe( arg, arg(), [ "a", "b" ], arg([ a ], moreFunc( with, some, args, [ "a" ] ) ) ); diff --git a/test/beautifier/function.js b/test/beautifier/function.js new file mode 100644 index 00000000..29ad58f8 --- /dev/null +++ b/test/beautifier/function.js @@ -0,0 +1,3 @@ +function spaces () { fixIt(); } +var func = function () { fixThisToo(); }; +callMe( arg, arg(), [ "a", "b" ], arg( [ a ], moreFunc( with, some, args, [ "a" ] ))); diff --git a/test/beautifier/if.expected.js b/test/beautifier/if.expected.js new file mode 100644 index 00000000..22057afa --- /dev/null +++ b/test/beautifier/if.expected.js @@ -0,0 +1,11 @@ +if ( someFunc() ) { + doStuff(); +} +if ( this ) { + doThis(); +} else { + doThat(); +} +if ( lotsOfSpace ) { + getRidOfIt(); +} diff --git a/test/beautifier/if.js b/test/beautifier/if.js new file mode 100644 index 00000000..13f30173 --- /dev/null +++ b/test/beautifier/if.js @@ -0,0 +1,3 @@ +if(someFunc()){doStuff();} +if(this){doThis();}else{doThat();} +if ( lotsOfSpace ) { getRidOfIt(); } diff --git a/test/beautifier/object.expected.js b/test/beautifier/object.expected.js new file mode 100644 index 00000000..98fcc97e --- /dev/null +++ b/test/beautifier/object.expected.js @@ -0,0 +1,15 @@ +{ + one: 1, + two: 2 +} +obj = { + spaces: "alot" +}; +func({ + spaceMe: "please" +}); +func({ + what: "now", + "1": a, + b: "beeeee" +}); diff --git a/test/beautifier/object.js b/test/beautifier/object.js new file mode 100644 index 00000000..aaf87d3b --- /dev/null +++ b/test/beautifier/object.js @@ -0,0 +1,7 @@ +{one:1,two:2} +obj = { spaces: "alot" }; +func({spaceMe:"please"}); +func( { + what : "now", "1": a, + b: "beeeee"} +); diff --git a/test/beautifier/test.sh b/test/beautifier/test.sh new file mode 100644 index 00000000..a013c7b4 --- /dev/null +++ b/test/beautifier/test.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +FILE1=./test/beautifier/${1} +FILE2=./test/beautifier/${2} +touch tmp.txt +python ./tools/jsbeautifier.py -s 2 -j -o tmp.txt --extra-expr-spacing=1 $FILE1 +bash tools/regex.sh tmp.txt +echo `diff tmp.txt $FILE2` diff --git a/test/beautifier/while.expected.js b/test/beautifier/while.expected.js new file mode 100644 index 00000000..36da5287 --- /dev/null +++ b/test/beautifier/while.expected.js @@ -0,0 +1,6 @@ +while ( something ) { + betterKeepDoingIt(); +} +while ( spaces ) { + getRidOfEm(); +} diff --git a/test/beautifier/while.js b/test/beautifier/while.js new file mode 100644 index 00000000..57d85f3e --- /dev/null +++ b/test/beautifier/while.js @@ -0,0 +1,4 @@ +while(something){betterKeepDoingIt();} + while ( spaces ) { + getRidOfEm(); + } diff --git a/test/butter.inject.js b/test/butter.inject.js new file mode 100644 index 00000000..411e4a16 --- /dev/null +++ b/test/butter.inject.js @@ -0,0 +1,27 @@ +/*global QUnit*/ +(function( global ) { + + var combineFn = function( first, second ) { + + first = first || function() {}; + second = second || function() {}; + + return function( message ) { + first.call( this, message ); + second.call( this, message ); + }; + }; + + if ( QUnit && global.parent ) { + QUnit.done = combineFn( QUnit.done, function( message ) { + global.parent.postMessage( JSON.stringify( message ), "*" ); + }); + QUnit.testDone = combineFn( QUnit.testDone, function( message ) { + global.parent.postMessage( JSON.stringify( message ), "*" ); + }); + // Fail tests that don't complete in 20s + QUnit.config.testTimeout = 20000; + QUnit.config.reorder = false; + } + +}( window )); diff --git a/test/butter.testrunner.css b/test/butter.testrunner.css new file mode 100644 index 00000000..cf4234e2 --- /dev/null +++ b/test/butter.testrunner.css @@ -0,0 +1,17 @@ +div#test-links { + padding-right: 15px; +} + +#test-links a { + margin-right: 5px; +} + +div#test-links > a { + font-size: 0.7em; +} + +iframe { + height: 600px; + width: 100%; + overflow: scroll; +} \ No newline at end of file diff --git a/test/butter.testrunner.js b/test/butter.testrunner.js new file mode 100644 index 00000000..7e8d1e92 --- /dev/null +++ b/test/butter.testrunner.js @@ -0,0 +1,239 @@ +(function() { + + var TestRunner = window.TestRunner = function() { + var id = function( name ) { + return document.getElementById( name ); + }, + create = function( type ) { + return document.createElement( type ); + }, + index = 0, + testFrame, + results = id( "qunit-tests" ), + totalPass = 0, + totalFail = 0, + totalRun = 0, + totalTime = 0, + mainLi = create( "li" ), + mainB = create( "b" ), + results_arr = [], + currentTest, + testList = [], + userAgent = id( "qunit-userAgent" ), + firstTest = true, + testBase; + + // Test base needs to be configured differently depending on if someone is running any of the test runners with Node or + // with Apache. When using Apache the assumption is made that their repo was named butter + if ( location.href.indexOf( "butter" ) ) { + testBase = location.href.substring( 0, location.href.indexOf( "butter" ) + 6 ) + "/"; + } + else { + testBase = location.protocol + "//" + location.hostname + ( location.port ? ":" + location.port : "" ); + } + + if ( userAgent ) { + userAgent.innerHTML = navigator.userAgent; + } + + function sendGetFocus( event ) { + if ( event.target && event.target.contentWindow ) { + event.target.contentWindow.postMessage( "getFocus", "*" ); + } + } + + function receiveResults( data ) { + var message = JSON.parse( data ), + li, + b, + ol, + a, + time, + type, + fail = 0, + pass = 0, + total = 0, + oneTest; + + // If name is present, we know this is a testDone post, so push results into array. + if ( message.name ) { + results_arr.push( message ); + } else { + + // this message is a Done post, so tally up everything and build the list item + ol = create( "ol" ); + ol.style.display = "none"; + + // build inner list of results + oneTest = results_arr.pop(); + while( oneTest ) { + li = create( "li" ); + li.className = oneTest.failed ? "fail" : "pass"; + li.innerHTML = oneTest.name + " (" + + oneTest.failed + ", " + + oneTest.passed + ", " + + oneTest.total + ")"; + ol.appendChild( li ); + // set to displayed if tests failed + if ( oneTest.failed ) { + ol.style.display = "block"; + } + oneTest = results_arr.pop(); + } + + a = create( "a" ); + a.innerHTML = "Run test in new window"; + a.href = currentTest.path; + a.target = "_blank"; + + fail = message.failed; + pass = message.passed; + total = message.total; + time = message.runtime; + + type = currentTest.name + " Tests"; + + mainB = create( "b" ); + mainB.innerHTML = "" + type + + " completed in " + + time + " milliseconds " + " (" + + fail + ", " + + pass + ", " + total + ")"; + + // set up click listener for expanding inner test list + mainB.addEventListener( "click", function( e ) { + var next = e.target.nextSibling.nextSibling, + display = next.style.display; + next.style.display = display === "none" ? "block" : "none"; + }, false ); + + // build mainLi, append all children and then append to result list + mainLi.className = fail ? "fail" : "pass"; + mainLi.removeChild( mainLi.firstChild ); + mainLi.appendChild( mainB ); + mainLi.appendChild( a ); + mainLi.appendChild( ol ); + + // update running totals + totalRun += total; + totalFail += fail; + totalPass += pass; + totalTime += time; + + advance(); + } + } + + function createFrame( testPath ) { + // Finish test suite; display totals + if ( !firstTest ) { + testFrame.parentNode.removeChild( testFrame ); + } + else { + firstTest = false; + } + testFrame = document.createElement( "iframe" ); + testFrame.onload = function() { + testFrame.contentWindow.focus(); + }; + // Tells the tests within the iframe to take focus + testFrame.addEventListener( "load", sendGetFocus, false ); + document.body.appendChild( testFrame ); + testFrame.src = currentTest.path; + } + + function advance() { + if ( ++index < testList.length ) { + currentTest = testList[ index ]; + mainLi = create( "li" ); + mainB = create ( "b" ); + mainB.innerHTML = "Running " + currentTest.name; + mainLi.appendChild( mainB ); + mainLi.className = "running"; + results.appendChild( mainLi ); + createFrame( currentTest.path ); + } else { + // Finish test suite; display totals + testFrame.parentNode.removeChild( testFrame ); + + id( "qunit-banner" ).className = totalFail ? "qunit-fail" : "qunit-pass"; + + var banner = create( "p" ), + html = [ + 'Tests completed in ', + totalTime, + ' milliseconds.
', + '', + totalPass, + ' tests of ', + totalRun, + ' passed, ', + totalFail, + ' failed.' + ].join(''); + + banner.id = "qunit-testresult"; + banner.className = "result"; + banner.innerHTML = html; + results.parentNode.insertBefore( banner, results ); + } + } + + this.getTests = function( testConf, loadedCallback ) { + var xhr = new XMLHttpRequest(); + + xhr.open( "GET", testConf, false ); + xhr.onreadystatechange = function() { + if ( xhr.readyState === 4 ) { + var allTests = JSON.parse( xhr.responseText ), + testGroup, + testLinks = id( "test-links" ), + anchor, + anchorText, + testName; + + for ( var x in allTests ) { + if ( allTests[ x ] ) { + testGroup = allTests[ x ]; + for ( var f in testGroup ) { + if ( testGroup[ f ] ) { + testList.push({ + "name": f.charAt( 0 ).toUpperCase() + f.slice( 1 ), + "path": testBase + testGroup[ f ], + "type": testGroup + }); + } + } + } + } + if ( loadedCallback && typeof loadedCallback === "function" ) { + loadedCallback(); + } + } + }; + xhr.send(); + }; + + this.runTests = function() { + if ( testList.length ) { + currentTest = testList[ index ]; + mainB.innerHTML = "Running " + currentTest.name; + mainLi.appendChild( mainB ); + mainLi.className = "running"; + results.appendChild( mainLi ); + + createFrame( currentTest.path ); + } + }; + + // Populate the userAgent h2 with information, if available + if ( userAgent ) { + userAgent.innerHTML = navigator.userAgent; + } + + // Triggers tallying of results, and advances the tests. + window.addEventListener( "message", function( e ) { + receiveResults( e.data ); + }); + }; +}()); diff --git a/test/config/index.html b/test/config/index.html new file mode 100644 index 00000000..36e88c7b --- /dev/null +++ b/test/config/index.html @@ -0,0 +1,175 @@ + + + + Butter Test Suite [Config] + + + + + + + + +

Butter API Test Suite [Config]

+

+
+

+
    + + diff --git a/test/controls.html b/test/controls.html new file mode 100644 index 00000000..df9cb2f3 --- /dev/null +++ b/test/controls.html @@ -0,0 +1,40 @@ + + + + Controls example page + + + + + + + +
    + +
    +
    + + diff --git a/test/core/core-debug/index.html b/test/core/core-debug/index.html new file mode 100644 index 00000000..cd82dc66 --- /dev/null +++ b/test/core/core-debug/index.html @@ -0,0 +1,52 @@ + + + + Butter Test Suite + + + + + + + + +

    Butter API Test Suite[ Core - Debug ]

    +

    +
    +

    +
      +
      +
      +
      + + diff --git a/test/core/core-dependency/core-dependency-tests.conf b/test/core/core-dependency/core-dependency-tests.conf new file mode 100644 index 00000000..50e47585 --- /dev/null +++ b/test/core/core-dependency/core-dependency-tests.conf @@ -0,0 +1,9 @@ +{ + "dependency": { + "dependency-auto-load-data": "test/core/core-dependency/dependency-auto-load-data.html?savedDataUrl=failure", + "dependency-auto-load-data-query-string": "test/core/core-dependency/dependency-auto-load-data-query-string.html?savedDataUrl=../saved-data-0000.json", + "dependency-load-css": "test/core/core-dependency/dependency-load-css.html", + "dependency-load-test-script": "test/core/core-dependency/dependency-load-test-script.html", + "dependency-plugin-loading": "test/core/core-dependency/dependency-plugin-loading.html" + } +} \ No newline at end of file diff --git a/test/core/core-dependency/dependency-auto-load-data-query-string.html b/test/core/core-dependency/dependency-auto-load-data-query-string.html new file mode 100644 index 00000000..7c04ca42 --- /dev/null +++ b/test/core/core-dependency/dependency-auto-load-data-query-string.html @@ -0,0 +1,44 @@ + + + + Butter Test Suite + + + + + + + + +

      Butter API Test Suite[ Core - Dependency ]

      +

      +
      +

      +
        +
        + + diff --git a/test/core/core-dependency/dependency-auto-load-data.html b/test/core/core-dependency/dependency-auto-load-data.html new file mode 100644 index 00000000..f40eb6e2 --- /dev/null +++ b/test/core/core-dependency/dependency-auto-load-data.html @@ -0,0 +1,44 @@ + + + + Butter Test Suite + + + + + + + + +

        Butter API Test Suite[ Core - Dependency ]

        +

        +
        +

        +
          +
          + + diff --git a/test/core/core-dependency/dependency-load-css.html b/test/core/core-dependency/dependency-load-css.html new file mode 100644 index 00000000..56aea78b --- /dev/null +++ b/test/core/core-dependency/dependency-load-css.html @@ -0,0 +1,51 @@ + + + + Butter Test Suite + + + + + + + + +

          Butter API Test Suite[ Core - Dependency ]

          +

          +
          +

          +
            +
            +
            +
            + + diff --git a/test/core/core-dependency/dependency-load-test-script.html b/test/core/core-dependency/dependency-load-test-script.html new file mode 100644 index 00000000..ea7eac38 --- /dev/null +++ b/test/core/core-dependency/dependency-load-test-script.html @@ -0,0 +1,47 @@ + + + + Butter Test Suite + + + + + + + + +

            Butter API Test Suite[ Core - Dependency ]

            +

            +
            +

            +
              +
              + + diff --git a/test/core/core-dependency/dependency-plugin-loading.html b/test/core/core-dependency/dependency-plugin-loading.html new file mode 100644 index 00000000..42370263 --- /dev/null +++ b/test/core/core-dependency/dependency-plugin-loading.html @@ -0,0 +1,29 @@ + + + + Butter Test Suite + + + + + + + + +

              Butter API Test Suite[ Core - Dependency ]

              +

              +
              +

              +
                +
                + + diff --git a/test/core/core-dependency/index.html b/test/core/core-dependency/index.html new file mode 100644 index 00000000..7bf9d3ff --- /dev/null +++ b/test/core/core-dependency/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                + Butter Test Suite[ Core - Dependency ] + +

                +

                +

                +
                  + + diff --git a/test/core/core-dialog/index.html b/test/core/core-dialog/index.html new file mode 100644 index 00000000..684338a9 --- /dev/null +++ b/test/core/core-dialog/index.html @@ -0,0 +1,54 @@ + + + + Butter Test Suite + + + + + + + + +

                  Butter API Test Suite[ Core - Dialog ]

                  +

                  +
                  +

                  +
                    +
                    + + diff --git a/test/core/core-import_export/core-import_export-tests.conf b/test/core/core-import_export/core-import_export-tests.conf new file mode 100644 index 00000000..bfab128f --- /dev/null +++ b/test/core/core-import_export/core-import_export-tests.conf @@ -0,0 +1,6 @@ +{ + "import-export": { + "html-escape": "test/core/core-import_export/import-export-html-escape.html", + "import-export": "test/core/core-import_export/import-export.html" + } +} \ No newline at end of file diff --git a/test/core/core-import_export/import-export-html-escape.html b/test/core/core-import_export/import-export-html-escape.html new file mode 100644 index 00000000..bd88be14 --- /dev/null +++ b/test/core/core-import_export/import-export-html-escape.html @@ -0,0 +1,43 @@ + + + + Butter Test Suite + + + + + + + + +

                    Butter API Test Suite[ Core - Exported HTML ]

                    +

                    +
                    +

                    +
                      +
                      +
                      +
                      + + diff --git a/test/core/core-import_export/import-export.html b/test/core/core-import_export/import-export.html new file mode 100644 index 00000000..bff5887d --- /dev/null +++ b/test/core/core-import_export/import-export.html @@ -0,0 +1,86 @@ + + + + Butter Test Suite + + + + + + + + +

                      Butter API Test Suite[ Core - Import/Export ]

                      +

                      +
                      +

                      +
                        +
                        + + diff --git a/test/core/core-import_export/index.html b/test/core/core-import_export/index.html new file mode 100644 index 00000000..b926d08a --- /dev/null +++ b/test/core/core-import_export/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                        + Butter Test Suite[ Core - Import/Export ] + +

                        +

                        +

                        +
                          + + diff --git a/test/core/core-media/core-media-tests.conf b/test/core/core-media/core-media-tests.conf new file mode 100644 index 00000000..61745a8d --- /dev/null +++ b/test/core/core-media/core-media-tests.conf @@ -0,0 +1,12 @@ +{ + "media": { + "media-create-object": "test/core/core-media/media-create-object.html", + "media-functionality": "test/core/core-media/media-functionality.html", + "media-manipulate-object": "test/core/core-media/media-manipulate-object.html", + "media-multiple-urls": "test/core/core-media/media-multiple-urls.html", + "media-multiple-urls-databuttersource": "test/core/core-media/media-multiple-urls-databuttersource.html", + "media-own-tracks": "test/core/core-media/media-own-tracks.html", + "media-targets": "test/core/core-media/media-targets.html", + "media-max-plugin-zindex": "test/core/core-media/media-max-plugin-zindex.html" + } +} diff --git a/test/core/core-media/index.html b/test/core/core-media/index.html new file mode 100644 index 00000000..faa40a60 --- /dev/null +++ b/test/core/core-media/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                          + Butter Test Suite[ Core - Media ] + +

                          +

                          +

                          +
                            + + diff --git a/test/core/core-media/media-create-object.html b/test/core/core-media/media-create-object.html new file mode 100644 index 00000000..b455d05d --- /dev/null +++ b/test/core/core-media/media-create-object.html @@ -0,0 +1,40 @@ + + + + Butter Test Suite + + + + + + + + +

                            Butter API Test Suite[ Core - Media ]

                            +

                            +
                            +

                            +
                              +
                              + +
                              +
                              +
                              +
                              +
                              +
                              + + diff --git a/test/core/core-media/media-functionality.html b/test/core/core-media/media-functionality.html new file mode 100644 index 00000000..99b92729 --- /dev/null +++ b/test/core/core-media/media-functionality.html @@ -0,0 +1,51 @@ + + + + Butter Test Suite + + + + + + + + +

                              Butter API Test Suite[ Core - Media ]

                              +

                              +
                              +

                              +
                                +
                                + + diff --git a/test/core/core-media/media-manipulate-object.html b/test/core/core-media/media-manipulate-object.html new file mode 100644 index 00000000..6e9277ac --- /dev/null +++ b/test/core/core-media/media-manipulate-object.html @@ -0,0 +1,103 @@ + + + + Butter Test Suite + + + + + + + + +

                                Butter API Test Suite[ Core - Media ]

                                +

                                +
                                +

                                +
                                  +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  +
                                  + + diff --git a/test/core/core-media/media-max-plugin-zindex.html b/test/core/core-media/media-max-plugin-zindex.html new file mode 100644 index 00000000..9231e7f8 --- /dev/null +++ b/test/core/core-media/media-max-plugin-zindex.html @@ -0,0 +1,46 @@ + + + + Butter Test Suite + + + + + + + + +

                                  Butter API Test Suite[ Core - Media ]

                                  +

                                  +
                                  +

                                  +
                                    +
                                    + + diff --git a/test/core/core-media/media-multiple-urls-databuttersource.html b/test/core/core-media/media-multiple-urls-databuttersource.html new file mode 100644 index 00000000..bcd8505c --- /dev/null +++ b/test/core/core-media/media-multiple-urls-databuttersource.html @@ -0,0 +1,39 @@ + + + + Butter Test Suite + + + + + + + + +

                                    Butter API Test Suite[ Core - Media ]

                                    +

                                    +
                                    +

                                    +
                                      +
                                      + + diff --git a/test/core/core-media/media-multiple-urls.html b/test/core/core-media/media-multiple-urls.html new file mode 100644 index 00000000..2d83d2fc --- /dev/null +++ b/test/core/core-media/media-multiple-urls.html @@ -0,0 +1,44 @@ + + + + Butter Test Suite + + + + + + + + +

                                      Butter API Test Suite[ Core - Media ]

                                      +

                                      +
                                      +

                                      +
                                        +
                                        + + diff --git a/test/core/core-media/media-own-tracks.html b/test/core/core-media/media-own-tracks.html new file mode 100644 index 00000000..ba6f4bef --- /dev/null +++ b/test/core/core-media/media-own-tracks.html @@ -0,0 +1,44 @@ + + + + Butter Test Suite + + + + + + + + +

                                        Butter API Test Suite[ Core - Media ]

                                        +

                                        +
                                        +

                                        +
                                          +
                                          + + diff --git a/test/core/core-media/media-targets.html b/test/core/core-media/media-targets.html new file mode 100644 index 00000000..32d79aa0 --- /dev/null +++ b/test/core/core-media/media-targets.html @@ -0,0 +1,58 @@ + + + + Butter Test Suite + + + + + + + + +

                                          Butter API Test Suite[ Core - Media ]

                                          +

                                          +
                                          +

                                          +
                                            +
                                            + +
                                            + + diff --git a/test/core/core-player/core-player-tests.conf b/test/core/core-player/core-player-tests.conf new file mode 100644 index 00000000..598c908e --- /dev/null +++ b/test/core/core-player/core-player-tests.conf @@ -0,0 +1,6 @@ +{ + "core-player": { + "basic": "test/core/core-player/player-basic.html", + "strange-element": "test/core/core-player/player-strange-element.html" + } +} \ No newline at end of file diff --git a/test/core/core-player/index.html b/test/core/core-player/index.html new file mode 100644 index 00000000..219091fd --- /dev/null +++ b/test/core/core-player/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                                            + Butter Test Suite[ Core - Player ] + +

                                            +

                                            +

                                            +
                                              + + diff --git a/test/core/core-player/player-basic.html b/test/core/core-player/player-basic.html new file mode 100644 index 00000000..8d986e6f --- /dev/null +++ b/test/core/core-player/player-basic.html @@ -0,0 +1,49 @@ + + + + Butter Test Suite + + + + + + + + +

                                              Butter API Test Suite[ Core - Player ]

                                              +

                                              +
                                              +

                                              +
                                                +
                                                +
                                                +
                                                + + diff --git a/test/core/core-player/player-strange-element.html b/test/core/core-player/player-strange-element.html new file mode 100644 index 00000000..735e5478 --- /dev/null +++ b/test/core/core-player/player-strange-element.html @@ -0,0 +1,43 @@ + + + + Butter Test Suite + + + + + + + + +

                                                Butter API Test Suite[ Core - Player ]

                                                +

                                                +
                                                +

                                                +
                                                  +
                                                  +
                                                  +
                                                  + + diff --git a/test/core/core-scripts/core-scripts-tests.conf b/test/core/core-scripts/core-scripts-tests.conf new file mode 100644 index 00000000..7662f661 --- /dev/null +++ b/test/core/core-scripts/core-scripts-tests.conf @@ -0,0 +1,6 @@ +{ + "scripts": { + "existence-execution": "test/core/core-scripts/scripts-existence-execution.html", + "none-callbacks": "test/core/core-scripts/scripts-none-callbacks.html" + } +} \ No newline at end of file diff --git a/test/core/core-scripts/index.html b/test/core/core-scripts/index.html new file mode 100644 index 00000000..02dae321 --- /dev/null +++ b/test/core/core-scripts/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                                                  + Butter Test Suite[ Core - Scripts ] + +

                                                  +

                                                  +

                                                  +
                                                    + + diff --git a/test/core/core-scripts/scripts-existence-execution.html b/test/core/core-scripts/scripts-existence-execution.html new file mode 100644 index 00000000..35ba22d7 --- /dev/null +++ b/test/core/core-scripts/scripts-existence-execution.html @@ -0,0 +1,47 @@ + + + + Butter Test Suite + + + + + + + + +

                                                    Butter API Test Suite[ Core - Scripts ]

                                                    +

                                                    +
                                                    +

                                                    +
                                                      +
                                                      +
                                                      +
                                                      + + diff --git a/test/core/core-scripts/scripts-none-callbacks.html b/test/core/core-scripts/scripts-none-callbacks.html new file mode 100644 index 00000000..45277e02 --- /dev/null +++ b/test/core/core-scripts/scripts-none-callbacks.html @@ -0,0 +1,47 @@ + + + + Butter Test Suite + + + + + + + + +

                                                      Butter API Test Suite[ Core - Scripts ]

                                                      +

                                                      +
                                                      +

                                                      +
                                                        +
                                                        +
                                                        +
                                                        + + diff --git a/test/core/core-target/core-target-tests.conf b/test/core/core-target/core-target-tests.conf new file mode 100644 index 00000000..99c37690 --- /dev/null +++ b/test/core/core-target/core-target-tests.conf @@ -0,0 +1,6 @@ +{ + "target": { + "creation-removal": "test/core/core-target/target-creation-removal.html", + "serialization": "test/core/core-target/target-serialization.html" + } +} \ No newline at end of file diff --git a/test/core/core-target/index.html b/test/core/core-target/index.html new file mode 100644 index 00000000..486f260a --- /dev/null +++ b/test/core/core-target/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                                                        + Butter Test Suite[ Core - Target ] + +

                                                        +

                                                        +

                                                        +
                                                          + + diff --git a/test/core/core-target/target-creation-removal.html b/test/core/core-target/target-creation-removal.html new file mode 100644 index 00000000..d5252d67 --- /dev/null +++ b/test/core/core-target/target-creation-removal.html @@ -0,0 +1,67 @@ + + + + Butter Test Suite + + + + + + + + +

                                                          Butter API Test Suite[ Core - Target ]

                                                          +

                                                          +
                                                          +

                                                          +
                                                            +
                                                            + + diff --git a/test/core/core-target/target-serialization.html b/test/core/core-target/target-serialization.html new file mode 100644 index 00000000..a2159d7b --- /dev/null +++ b/test/core/core-target/target-serialization.html @@ -0,0 +1,50 @@ + + + + Butter Test Suite + + + + + + + + +

                                                            Butter API Test Suite[ Core - Target ]

                                                            +

                                                            +
                                                            +

                                                            +
                                                              +
                                                              + + diff --git a/test/core/core-track/core-track-tests.conf b/test/core/core-track/core-track-tests.conf new file mode 100644 index 00000000..1824f8cb --- /dev/null +++ b/test/core/core-track/core-track-tests.conf @@ -0,0 +1,6 @@ +{ + "core-track": { + "creation": "test/core/core-track/track-creation.html", + "management": "test/core/core-track/track-management.html" + } +} \ No newline at end of file diff --git a/test/core/core-track/index.html b/test/core/core-track/index.html new file mode 100644 index 00000000..d83d7d1e --- /dev/null +++ b/test/core/core-track/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                                                              + Butter Test Suite[ Core - Track ] + +

                                                              +

                                                              +

                                                              +
                                                                + + diff --git a/test/core/core-track/track-creation.html b/test/core/core-track/track-creation.html new file mode 100644 index 00000000..e411ee20 --- /dev/null +++ b/test/core/core-track/track-creation.html @@ -0,0 +1,32 @@ + + + + Butter Test Suite + + + + + + + + +

                                                                Butter API Test Suite[ Core - Track ]

                                                                +

                                                                +
                                                                +

                                                                +
                                                                  +
                                                                  + + diff --git a/test/core/core-track/track-management.html b/test/core/core-track/track-management.html new file mode 100644 index 00000000..cfe60dcc --- /dev/null +++ b/test/core/core-track/track-management.html @@ -0,0 +1,69 @@ + + + + Butter Test Suite + + + + + + + + +

                                                                  Butter API Test Suite[ Core - Track ]

                                                                  +

                                                                  +
                                                                  +

                                                                  +
                                                                    +
                                                                    + + diff --git a/test/core/core-trackevent/core-trackevent-tests.conf b/test/core/core-trackevent/core-trackevent-tests.conf new file mode 100644 index 00000000..1cac7717 --- /dev/null +++ b/test/core/core-trackevent/core-trackevent-tests.conf @@ -0,0 +1,10 @@ +{ + "core-trackevent": { + "creation": "test/core/core-trackevent/trackevent-creation.html", + "defaults": "test/core/core-trackevent/trackevent-defaults.html", + "getTrackEvents": "test/core/core-trackevent/trackevent-getTrackEvents.html", + "getTrackEventsByType": "test/core/core-trackevent/trackevent-getTrackEventsByType.html", + "management": "test/core/core-trackevent/trackevent-management.html", + "remove-add-constituent": "test/core/core-trackevent/trackevent-remove-add-constituent.html" + } +} \ No newline at end of file diff --git a/test/core/core-trackevent/index.html b/test/core/core-trackevent/index.html new file mode 100644 index 00000000..9c1b9705 --- /dev/null +++ b/test/core/core-trackevent/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                                                                    + Butter Test Suite[ Core - Track Event ] + +

                                                                    +

                                                                    +

                                                                    +
                                                                      + + diff --git a/test/core/core-trackevent/trackevent-creation.html b/test/core/core-trackevent/trackevent-creation.html new file mode 100644 index 00000000..f4a8a07f --- /dev/null +++ b/test/core/core-trackevent/trackevent-creation.html @@ -0,0 +1,85 @@ + + + + Butter Test Suite + + + + + + + + +

                                                                      Butter API Test Suite[ Core - Track Event ]

                                                                      +

                                                                      +
                                                                      +

                                                                      +
                                                                        +
                                                                        +
                                                                        +
                                                                        +
                                                                        + + diff --git a/test/core/core-trackevent/trackevent-defaults.html b/test/core/core-trackevent/trackevent-defaults.html new file mode 100644 index 00000000..dc0f13b2 --- /dev/null +++ b/test/core/core-trackevent/trackevent-defaults.html @@ -0,0 +1,50 @@ + + + + Butter Test Suite + + + + + + + + +

                                                                        Butter API Test Suite[ Core - Track Event ]

                                                                        +

                                                                        +
                                                                        +

                                                                        +
                                                                          +
                                                                          +
                                                                          +
                                                                          + + diff --git a/test/core/core-trackevent/trackevent-getTrackEvents.html b/test/core/core-trackevent/trackevent-getTrackEvents.html new file mode 100644 index 00000000..63eb58a8 --- /dev/null +++ b/test/core/core-trackevent/trackevent-getTrackEvents.html @@ -0,0 +1,38 @@ + + + + Butter Test Suite + + + + + + + + +

                                                                          Butter API Test Suite[ Core - Track Event ]

                                                                          +

                                                                          +
                                                                          +

                                                                          +
                                                                            +
                                                                            +
                                                                            +
                                                                            + + diff --git a/test/core/core-trackevent/trackevent-getTrackEventsByType.html b/test/core/core-trackevent/trackevent-getTrackEventsByType.html new file mode 100644 index 00000000..8a8b8511 --- /dev/null +++ b/test/core/core-trackevent/trackevent-getTrackEventsByType.html @@ -0,0 +1,37 @@ + + + + Butter Test Suite + + + + + + + + +

                                                                            Butter API Test Suite[ Core - Track Event ]

                                                                            +

                                                                            +
                                                                            +

                                                                            +
                                                                              +
                                                                              +
                                                                              +
                                                                              + + diff --git a/test/core/core-trackevent/trackevent-management.html b/test/core/core-trackevent/trackevent-management.html new file mode 100644 index 00000000..4a45c0cd --- /dev/null +++ b/test/core/core-trackevent/trackevent-management.html @@ -0,0 +1,66 @@ + + + + Butter Test Suite + + + + + + + + +

                                                                              Butter API Test Suite[ Core - Track Event ]

                                                                              +

                                                                              +
                                                                              +

                                                                              +
                                                                                +
                                                                                +
                                                                                +
                                                                                + + diff --git a/test/core/core-trackevent/trackevent-remove-add-constituent.html b/test/core/core-trackevent/trackevent-remove-add-constituent.html new file mode 100644 index 00000000..498f2064 --- /dev/null +++ b/test/core/core-trackevent/trackevent-remove-add-constituent.html @@ -0,0 +1,61 @@ + + + + Butter Test Suite + + + + + + + + +

                                                                                Butter API Test Suite[ Core - Track Event ]

                                                                                +

                                                                                +
                                                                                +

                                                                                +
                                                                                  +
                                                                                  +
                                                                                  +
                                                                                  + + diff --git a/test/core/sample.oga b/test/core/sample.oga new file mode 100644 index 00000000..ba3c9af0 Binary files /dev/null and b/test/core/sample.oga differ diff --git a/test/core/saved-data-0000.json b/test/core/saved-data-0000.json new file mode 100644 index 00000000..c84533a2 --- /dev/null +++ b/test/core/saved-data-0000.json @@ -0,0 +1,46 @@ +{ + "targets": [ + { + "id": "Target0", + "name": "Target1", + "element": "test-target-1" + } + ], + "media": [ + { + "id": "Media0", + "name": "Media0", + "url": [ + "../../external/popcorn-js/test/trailer.ogv", + "../../external/popcorn-js/test/trailer.mp4" + ], + "target": "mediaDiv", + "duration": 64.54166412353516, + "tracks": [ + { + "name": "Track0", + "id": "Track0", + "trackEvents": [] + }, + { + "name": "Track1", + "id": "Track1", + "trackEvents": [ + { + "id": "TrackEvent0", + "type": "text", + "popcornOptions": { + "start": 0, + "end": 3, + "text": "test", + "target": "test-target-1" + }, + "track": "Track0", + "name": "TrackEvent0" + } + ] + } + ] + } + ] +} diff --git a/test/core/test-after.js b/test/core/test-after.js new file mode 100644 index 00000000..656d2a0e --- /dev/null +++ b/test/core/test-after.js @@ -0,0 +1,5 @@ +/* Note: This code is just for testing script functionality on Butter init. + * Disregard unless you want advanced functionality. */ +(function(){ + "inline test after"; +}()); \ No newline at end of file diff --git a/test/core/test-before.js b/test/core/test-before.js new file mode 100644 index 00000000..f758c0e6 --- /dev/null +++ b/test/core/test-before.js @@ -0,0 +1,5 @@ +/* Note: This code is just for testing script functionality on Butter init. + * Disregard unless you want advanced functionality. */ +(function(){ + "inline test before"; +}()); \ No newline at end of file diff --git a/test/core/test-config-auto-load-2.json b/test/core/test-config-auto-load-2.json new file mode 100644 index 00000000..1768be34 --- /dev/null +++ b/test/core/test-config-auto-load-2.json @@ -0,0 +1,21 @@ +{ + "name": "test-config-auto-load", + "makeVideoURLsUnique": false, + "baseDir": "../../../", + "scrapePage": false, + "ui": { + "enabled": false + }, + "plugin": { + "plugins": [ + { + "type": "text", + "path": "{{baseDir}}external/popcorn-js/plugins/text/popcorn.text.js" + }, + { + "type": "image", + "path": "{{baseDir}}external/popcorn-js/plugins/image/popcorn.image.js" + } + ] + } +} diff --git a/test/core/test-config-auto-load.json b/test/core/test-config-auto-load.json new file mode 100644 index 00000000..8f4ba043 --- /dev/null +++ b/test/core/test-config-auto-load.json @@ -0,0 +1,22 @@ +{ + "name": "test-config-auto-load", + "makeVideoURLsUnique": false, + "baseDir": "../../../", + "savedDataUrl": "../saved-data-0000.json", + "scrapePage": false, + "ui": { + "enabled": false + }, + "plugin": { + "plugins": [ + { + "type": "text", + "path": "{{baseDir}}external/popcorn-js/plugins/text/popcorn.text.js" + }, + { + "type": "image", + "path": "{{baseDir}}external/popcorn-js/plugins/image/popcorn.image.js" + } + ] + } +} diff --git a/test/core/test-config-core.json b/test/core/test-config-core.json new file mode 100644 index 00000000..0657f793 --- /dev/null +++ b/test/core/test-config-core.json @@ -0,0 +1,43 @@ +{ + "name": "test-config", + "baseDir": "../../../", + "debug": true, + "scrapePage": false, + "makeVideoURLsUnique": false, + "recover": "purge", + "ui": { + "enabled": false, + "trackEventHighlight": "click" + }, + "editor": { + }, + "plugin": { + "plugins": [ + { + "type": "text", + "path": "{{baseDir}}external/popcorn-js/plugins/text/popcorn.text.js" + }, + { + "type": "image", + "path": "{{baseDir}}external/popcorn-js/plugins/image/popcorn.image.js" + } + ], + "defaults": [ + "text", + "image" + ] + }, + "popcorn": { + "scripts": { + "init": "#init-script", + "beforeEvents": "../test-before.js", + "afterEvents": "../test-after.js" + }, + "callbacks": { + "init": "_testInitCallback", + "beforeEvents": "_testBeforeCallback", + "afterEvents": "_testAfterCallback" + } + }, + "maxPluginZIndex": 1000 +} diff --git a/test/core/test-css.css b/test/core/test-css.css new file mode 100644 index 00000000..b9b85075 --- /dev/null +++ b/test/core/test-css.css @@ -0,0 +1,7 @@ +#css-test { + position: absolute; + width: 100px; + height: 100px; + right: 0; + top: 0; +} \ No newline at end of file diff --git a/test/core/test-init.js b/test/core/test-init.js new file mode 100644 index 00000000..89e3087a --- /dev/null +++ b/test/core/test-init.js @@ -0,0 +1,5 @@ +/* Note: This code is just for testing script functionality on Butter init. + * Disregard unless you want advanced functionality. */ +(function(){ + "inline test init"; +}()); \ No newline at end of file diff --git a/test/core/test-override-config.json b/test/core/test-override-config.json new file mode 100644 index 00000000..fb83b8d9 --- /dev/null +++ b/test/core/test-override-config.json @@ -0,0 +1,26 @@ +{ + "name": "test-override-config", + "debug": true, + "baseDir": "../../", + "makeVideoURLsUnique": false, + "ui": { + "enabled": false, + "trackEventHighlight": "click" + }, + "plugin": { + "plugins": [ + { + "type": "text", + "path": "{{baseDir}}external/popcorn-js/plugins/text/popcorn.text.js" + }, + { + "type": "image", + "path": "{{baseDir}}external/popcorn-js/plugins/image/popcorn.image.js" + } + ], + "defaults": [ + "text", + "image" + ] + } +} diff --git a/test/core/test-script.js b/test/core/test-script.js new file mode 100644 index 00000000..04b7fb7d --- /dev/null +++ b/test/core/test-script.js @@ -0,0 +1,4 @@ +if( !window.__testScript ){ + window.__testScript = []; +} +window.__testScript.push( true ); diff --git a/test/core/test-simple-config.json b/test/core/test-simple-config.json new file mode 100644 index 00000000..9872e6d6 --- /dev/null +++ b/test/core/test-simple-config.json @@ -0,0 +1,6 @@ +{ + "baseDir": "../../../", + "ui": { + "enabled": false + } +} diff --git a/test/cornfield/cornfield-test-config.json b/test/cornfield/cornfield-test-config.json new file mode 100644 index 00000000..3f6e010d --- /dev/null +++ b/test/cornfield/cornfield-test-config.json @@ -0,0 +1,17 @@ +{ + "ui": { + "enabled": false, + "trackEventHighlight": "click" + }, + "plugin": { + }, + "icons": { + "default": "../../resources/popcorn-icon.png", + "image": "../../resources/image-icon.png" + }, + "dirs": { + "popcorn-js": "../../external/popcorn-js/" + } +} + + diff --git a/test/cornfield/cornfield.js b/test/cornfield/cornfield.js new file mode 100644 index 00000000..f3c8d07a --- /dev/null +++ b/test/cornfield/cornfield.js @@ -0,0 +1,180 @@ +/*global text,expect,ok,module,notEqual,test,window*/ + +/* This Source Code Form is subject to the terms of the MIT license + * If a copy of the MIT license was not distributed with this file, you can + * obtain one at http://www.mozillapopcorn.org/butter-license.txt */ + +( function ( window, document, undefined ){ + + QUnit.config.reorder = false; + + document.addEventListener( "DOMContentLoaded", function(){ + + Butter.init({ + config: "cornfield-test-config.json", + ready: function( butter ) { + var filename = "test" + Date.now(), + data = { + name: filename, + html: "herpderp", + data: { stuff: "derpherp" }, + template: "popup" + }, + stringedData = JSON.stringify( data ); + + module( "Server State" ); + + // Make sure the cornfield server is running before bothering with all these tests. + asyncTest( "Environment ", 1, function() { + var request = new XMLHttpRequest(); + request.open( 'GET', '/api/whoami', false ); + request.send(); + if ( request.status === 404 ) { + ok( false, "Cornfield server not running on node server, skipping tests." ); + } else { + ok( true, "Cornfield server is running on node server, running Tests." ); + } + start(); + }); + + module( "Unauthenticated tests" ); + + asyncTest( "Logout", 1, function() { + butter.cornfield.logout( function( res ) { + equal( res, true, "Clean-up" ); + + start(); + }); + }); + + test( "Sync API", 4, function() { + ok( !butter.cornfield.authenticated(), "Authenticated is false" ); + ok( !butter.cornfield.email(), "Email is undefined" ); + ok( !butter.cornfield.name(), "Name is undefined" ); + ok( !butter.cornfield.username(), "Username is undefined" ); + }); + + asyncTest( "Async API", 3, function() { + + butter.cornfield.list( function( res ) { + deepEqual( res, { error: "unauthorized" }, "Not allowed to list projects" ); + + butter.cornfield.load( filename, function( res ) { + deepEqual( res, { error: "unauthorized" }, "Not allowed to get projects" ); + + butter.cornfield.save( filename, stringedData, function( res ) { + deepEqual( res, { error: "unauthorized" }, "Not allowed to save projects" ); + + start(); + }); + }); + }); + }); + + asyncTest( "/api/whoami", 1, function() { + butter.cornfield.whoami( function( res ) { + deepEqual( res, { error: "unauthorized" }, "Response is unauthorized" ); + start(); + }); + }); + + module( "Authentication tests" ); + + asyncTest( "Login (user input needed)", 6, function() { + butter.cornfield.login( function( res ) { + clearTimeout( failSafe ); + ok( res, "The login response has data" ); + ok( !res.error, "okay", "No errors while logging in" ); + ok( res.email, "The login has an email: " + res.email ); + equal( res.email, butter.cornfield.email(), "Email is stored" ); + equal( res.name, butter.cornfield.name(), "Name is stored" ); + equal( res.username, butter.cornfield.username(), "Name is stored" ); + start(); + }); + + var failSafe = setTimeout( function() { + clearTimeout( failSafe ); + start(); + }, 20000 ); + }); + + module( "Authenticated tests" ); + + test( "Sync API", 4, function() { + ok( butter.cornfield.authenticated(), "Authenticated is true" ); + ok( butter.cornfield.email(), "Email is a truthy value" ); + ok( butter.cornfield.name(), "Name is a truthy value" ); + ok( butter.cornfield.username(), "Username is a truthy value" ); + }); + + asyncTest( "Async API", 7, function() { + var foundProject = false; + + butter.cornfield.list( function( res ) { + ok(res, "The project list response has data" ); + equal( res.error, "okay", "Project list status is \"okay\"" ); + ok( res.projects, "There is a list of projects" ); + + for( i = 0, len = res.projects.length; i < len; i++ ){ + if( res.projects[ i ].name === filename ){ + foundProject = true; + break; + } + } + + equal( false, foundProject, filename + " is not present in the projects list" ); + + butter.cornfield.load( filename, function( res ) { + deepEqual( res, { error: "project not found" }, "The project load response is project not found" ); + + butter.cornfield.save( filename, stringedData, function( res ) { + equal( res.error, "okay", "The project save response is okay" ); + + filename = res.project._id; + + butter.cornfield.load( filename, function( res ) { + equal( res.stuff, data.data.stuff, "The project is the same" ); + + start(); + }); + }); + }); + }); + + var failSafe = setTimeout( function() { + clearTimeout( failSafe ); + start(); + }, 20000 ); + }); + + asyncTest( "/api/whoami", 1, function() { + butter.cornfield.whoami( function( res ) { + deepEqual( res, { + email: butter.cornfield.email(), + name: butter.cornfield.email(), + username: butter.cornfield.email() + }, "Response contains user information" ); + start(); + }); + }); + + asyncTest( "Logout", 1, function() { + butter.cornfield.logout( function( res ) { + equal( res, true, "Clean-up" ); + + start(); + }); + }); + + module( "State after logout" ); + + test( "Sync API", 4, function() { + ok( !butter.cornfield.authenticated(), "Authenticated is false" ); + ok( !butter.cornfield.email(), "Email is undefined" ); + ok( !butter.cornfield.name(), "Name is undefined" ); + ok( !butter.cornfield.username(), "Username is undefined" ); + }); + } + }); + }, false ); +}( window, window.document )); diff --git a/test/cornfield/index.html b/test/cornfield/index.html new file mode 100644 index 00000000..5af53e4f --- /dev/null +++ b/test/cornfield/index.html @@ -0,0 +1,33 @@ + + + + + + Butter Test Suite + + + + + + + + + + + +

                                                                                  Butter API Test Suite

                                                                                  +

                                                                                  +
                                                                                  +

                                                                                  +
                                                                                    + + + diff --git a/test/editor/editor-basic-usage.html b/test/editor/editor-basic-usage.html new file mode 100644 index 00000000..c34d9f59 --- /dev/null +++ b/test/editor/editor-basic-usage.html @@ -0,0 +1,48 @@ + + + + Butter Test Suite [Editor Module] + + + + + + + + + +

                                                                                    Butter API Test Suite [Editor Module]

                                                                                    +

                                                                                    +
                                                                                    +

                                                                                    +
                                                                                      +
                                                                                      + + diff --git a/test/editor/editor-createStartEndInputs.html b/test/editor/editor-createStartEndInputs.html new file mode 100644 index 00000000..284ec491 --- /dev/null +++ b/test/editor/editor-createStartEndInputs.html @@ -0,0 +1,63 @@ + + + + Butter Test Suite [Editor Module] + + + + + + + + + +

                                                                                      Butter API Test Suite [Editor Module]

                                                                                      +

                                                                                      +
                                                                                      +

                                                                                      +
                                                                                        +
                                                                                        + + diff --git a/test/editor/editor-custom.html b/test/editor/editor-custom.html new file mode 100644 index 00000000..c715901c --- /dev/null +++ b/test/editor/editor-custom.html @@ -0,0 +1,97 @@ + + + + Butter Test Suite [Editor Module] + + + + + + + + + +

                                                                                        Butter API Test Suite [Editor Module]

                                                                                        +

                                                                                        +
                                                                                        +

                                                                                        +
                                                                                          +
                                                                                          + + diff --git a/test/editor/editor-tests.conf b/test/editor/editor-tests.conf new file mode 100644 index 00000000..ae8dcfdf --- /dev/null +++ b/test/editor/editor-tests.conf @@ -0,0 +1,7 @@ +{ + "editor": { + "basic-usage": "test/editor/editor-basic-usage.html", + "custom": "test/editor/editor-custom.html", + "createStartEndInputs": "test/editor/editor-createStartEndInputs.html" + } +} \ No newline at end of file diff --git a/test/editor/index.html b/test/editor/index.html new file mode 100644 index 00000000..454e51f9 --- /dev/null +++ b/test/editor/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                                                                                          + Butter Test Suite [ Editor ] + +

                                                                                          +

                                                                                          +

                                                                                          +
                                                                                            + + diff --git a/test/eventmanager/index.html b/test/eventmanager/index.html new file mode 100644 index 00000000..387d6bf6 --- /dev/null +++ b/test/eventmanager/index.html @@ -0,0 +1,198 @@ + + + + Butter Test Suite [EventManagerWrapper] + + + + + \ + + + +

                                                                                            Butter API Test Suite [EventManagerWrapper]

                                                                                            +

                                                                                            +
                                                                                            +

                                                                                            +
                                                                                              + + diff --git a/test/index.html b/test/index.html new file mode 100644 index 00000000..019af9dd --- /dev/null +++ b/test/index.html @@ -0,0 +1,51 @@ + + + + Butter Test Suite Runner + + + + + + +

                                                                                              + Butter Test Suite + +

                                                                                              +

                                                                                              +

                                                                                              +
                                                                                                + + diff --git a/test/inject.js b/test/inject.js new file mode 100644 index 00000000..845854eb --- /dev/null +++ b/test/inject.js @@ -0,0 +1,447 @@ +/** + * JavaScript file to included by the test suite page that it loaded + * inside the iframe on the "run" pages. This injection must be done + * by the guest page, it can't be loaded by TestSwarm. + * Example: + * - https://github.com/jquery/jquery/blob/master/test/data/testrunner.js + * - https://github.com/jquery/jquery/blob/master/test/index.html + * + * @author John Resig, 2008-2011 + * @author Timo Tijhof, 2012 + * @since 0.1.0 + * @package TestSwarm + */ +/*global jQuery, $, QUnit, Test, JSSpec, JsUnitTestManager, SeleniumTestResult, LOG, doh, Screw*/ +/*jshint forin:false, strict:false, loopfunc:true, browser:true, jquery:true*/ +(function (undefined) { + var DEBUG, doPost, search, url, index, submitTimeout, curHeartbeat, + beatRate, testFrameworks, onErrorFnPrev; + + DEBUG = false; + + doPost = false; + search = window.location.search; + index = search.indexOf( 'swarmURL=' ); + submitTimeout = 5; + beatRate = 20; + + try { + doPost = !!window.parent.postMessage; + } catch ( e ) {} + + if ( index !== -1 ) { + url = decodeURIComponent( search.slice( index + 9 ) ); + } + + if ( !DEBUG && ( !url || url.indexOf( 'http' ) !== 0 ) ) { + return; + } + + // Prevent blocking things from executing + if ( !DEBUG ) { + window.print = window.confirm = window.alert = window.open = function () {}; + } + + /** Utility functions **/ + + function debugObj( obj ) { + var i, str = ''; + for ( i in obj ) { + str += ( str ? '\n' : '' ) + i + ':\n\t ' + obj[i]; + } + return str; + } + + function remove( elem ) { + if ( typeof elem === 'string' ) { + elem = document.getElementById( elem ); + } + + if ( elem ) { + elem.parentNode.removeChild( elem ); + } + } + + function trimSerialize( doc ) { + var scripts, root, cur, links, i, href; + doc = doc || document; + + scripts = doc.getElementsByTagName( 'script' ); + while ( scripts.length ) { + remove( scripts[0] ); + } + + root = window.location.href.replace( /(https?:\/\/.*?)\/.*/, '$1' ); + cur = window.location.href.replace( /[^\/]*$/, '' ); + + links = doc.getElementsByTagName( 'link' ); + for ( i = 0; i < links.length; i += 1 ) { + href = links[i].href; + if ( href.indexOf( '/' ) === 0 ) { + href = root + href; + } else if ( !/^https?:\/\//.test( href ) ) { + href = cur + href; + } + links[i].href = href; + } + + return ( '' + doc.documentElement.innerHTML + '' ) + .replace( /\s+/g, ' ' ); + } + + function submit( params ) { + var form, i, input, key, paramItems, parts, query; + + if ( curHeartbeat ) { + clearTimeout( curHeartbeat ); + } + + paramItems = (url.split( '?' )[1] || '' ).split( '&' ); + + for ( i = 0; i < paramItems.length; i += 1 ) { + if ( paramItems[i] ) { + parts = paramItems[i].split( '=' ); + if ( !params[ parts[0] ] ) { + params[ parts[0] ] = parts[1]; + } + } + } + + if ( !params.action ) { + params.action = 'saverun'; + } + + if ( !params.report_html ) { + params.report_html = window.TestSwarm.serialize(); + } + + if ( DEBUG ) { + alert( debugObj( params ) ) ; + } + + if ( doPost ) { + // Build Query String + query = ''; + + for ( key in params ) { + query += ( query ? '&' : '' ) + key + '=' + encodeURIComponent( params[key] ); + } + + if ( !DEBUG ) { + window.parent.postMessage( query, '*' ); + } + + } else { + form = document.createElement( 'form' ); + form.action = url; + form.method = 'POST'; + + for ( i in params ) { + input = document.createElement( 'input' ); + input.type = 'hidden'; + input.name = i; + input.value = params[i]; + form.appendChild( input ); + } + + if ( DEBUG ) { + alert( url ); + + } else { + // Watch for the result submission timing out + setTimeout(function () { + submit( params ); + }, submitTimeout * 1000); + + document.body.appendChild( form ); + form.submit(); + } + } + } + + function detectAndInstall() { + var key; + for ( key in testFrameworks ) { + if ( testFrameworks[key].detect() ) { + testFrameworks[key].install(); + return key; + } + } + return false; + } + + // Preserve other handlers + onErrorFnPrev = window.onerror; + + // Cover uncaught exceptions + // Returning true will surpress the default browser handler, + // returning false will let it run. + window.onerror = function ( error, filePath, linerNr ) { + var ret = false; + if ( onErrorFnPrev ) { + ret = onErrorFnPrev( error, filePath, linerNr ); + } + + // Treat return value as window.onerror itself does, + // Only do our handling if not surpressed. + if ( ret !== true ) { + document.body.appendChild( document.createTextNode( '[TestSwarm] window.onerror: ' + error ) ); + submit({ fail: 0, error: 1, total: 1 }); + + return false; + } + + return ret; + }; + + // Expose the TestSwarm API + window.TestSwarm = { + submit: submit, + heartbeat: function () { + if ( curHeartbeat ) { + clearTimeout( curHeartbeat ); + } + + curHeartbeat = setTimeout(function () { + submit({ fail: -1, total: -1 }); + }, beatRate * 1000); + }, + serialize: function () { + return trimSerialize(); + } + }; + + testFrameworks = { + // QUnit (by jQuery) + // http://docs.jquery.com/QUnit + 'QUnit': { + detect: function () { + return typeof QUnit !== 'undefined'; + }, + install: function () { + QUnit.done = function ( results ) { + submit({ + fail: results.failed, + error: 0, + total: results.total + }); + }; + + QUnit.log = window.TestSwarm.heartbeat; + window.TestSwarm.heartbeat(); + + window.TestSwarm.serialize = function () { + var ol, i; + + // Clean up the HTML (remove any un-needed test markup) + remove( 'nothiddendiv' ); + remove( 'loadediframe' ); + remove( 'dl' ); + remove( 'main' ); + + // Show any collapsed results + ol = document.getElementsByTagName( 'ol' ); + for ( i = 0; i < ol.length; i += 1 ) { + ol[i].style.display = 'block'; + } + + return trimSerialize(); + }; + } + }, + + // UnitTestJS (Prototype, Scriptaculous) + // https://github.com/tobie/unittest_js + 'UnitTestJS': { + detect: function () { + return typeof Test !== 'undefined' && Test && Test.Unit && Test.Unit.runners; + }, + install: function () { + var total_runners = Test.Unit.runners.length, + cur_runners = 0, + total = 0, + fail = 0, + error = 0, + i; + + for ( i = 0; i < Test.Unit.runners.length; i += 1 ) { + // Need to proxy the i variable into a local scope, + // otherwise all the finish-functions created in this loop + // will refer to the same i variable.. + (function ( i ) { + var finish, results; + + finish = Test.Unit.runners[i].finish; + Test.Unit.runners[i].finish = function () { + finish.call( this ); + + results = this.getResult(); + total += results.assertions; + fail += results.failures; + error += results.errors; + + cur_runners += 1; + if ( cur_runners === total_runners ) { + submit({ + fail: fail, + error: error, + total: total + }); + } + }; + }( i ) ); + } + } + }, + + // JSSpec (MooTools) + // http://jania.pe.kr/aw/moin.cgi/JSSpec + // https://code.google.com/p/jsspec/ + 'JSSpec': { + detect: function () { + return typeof JSSpec !== 'undefined' && JSSpec && JSSpec.Logger; + }, + install: function () { + var onRunnerEnd = JSSpec.Logger.prototype.onRunnerEnd; + JSSpec.Logger.prototype.onRunnerEnd = function () { + var ul, i; + onRunnerEnd.call( this ); + + // Show any collapsed results + ul = document.getElementsByTagName( 'ul' ); + for ( i = 0; i < ul.length; i += 1 ) { + ul[i].style.display = 'block'; + } + + submit({ + fail: JSSpec.runner.getTotalFailures(), + error: JSSpec.runner.getTotalErrors(), + total: JSSpec.runner.totalExamples + }); + }; + + window.TestSwarm.serialize = function () { + var ul, i; + // Show any collapsed results + ul = document.getElementsByTagName( 'ul' ); + for ( i = 0; i < ul.length; i += 1 ) { + ul[i].style.display = 'block'; + } + + return trimSerialize(); + }; + } + }, + + // JSUnit + // http://www.jsunit.net/ + // Note: Injection file must be included before the frames + // are document.write()d into the page. + 'JSUnit': { + detect: function () { + return typeof JsUnitTestManager !== 'undefined'; + }, + install: function () { + var _done = JsUnitTestManager.prototype._done; + JsUnitTestManager.prototype._done = function () { + _done.call( this ); + + submit({ + fail: this.failureCount, + error: this.errorCount, + total: this.totalCount + }); + }; + + window.TestSwarm.serialize = function () { + return '
                                                                                                ' + this.log.join( '\n' ) + '
                                                                                                '; + }; + } + }, + + // Selenium Core + // http://seleniumhq.org/projects/core/ + 'Selenium': { + detect: function () { + return typeof SeleniumTestResult !== 'undefined' && typeof LOG !== 'undefined'; + }, + install: function () { + // Completely overwrite the postback + SeleniumTestResult.prototype.post = function () { + submit({ + fail: this.metrics.numCommandFailures, + error: this.metrics.numCommandErrors, + total: this.metrics.numCommandPasses + this.metrics.numCommandFailures + this.metrics.numCommandErrors + }); + }; + + window.TestSwarm.serialize = function () { + var results = [], msg; + while ( LOG.pendingMessages.length ) { + msg = LOG.pendingMessages.shift(); + results.push( msg.type + ': ' + msg.msg ); + } + + return '
                                                                                                ' + results.join( '\n' ) + '
                                                                                                '; + }; + } + }, + + // Dojo Objective Harness + // http://docs.dojocampus.org/quickstart/doh + 'DOH': { + detect: function () { + return typeof doh !== 'undefined' && doh._report; + }, + install: function () { + var _report = doh._report; + doh._report = function () { + _report.apply( this, arguments ); + + submit({ + fail: doh._failureCount, + error: doh._errorCount, + total: doh._testCount + }); + }; + + window.TestSwarm.serialize = function () { + return '
                                                                                                ' + document.getElementById( 'logBody' ).innerHTML + '
                                                                                                '; + }; + } + }, + + // Screw.Unit + // https://github.com/nathansobo/screw-unit + 'Screw.Unit': { + detect: function () { + return typeof Screw !== 'undefined' && typeof jQuery !== 'undefined' && Screw && Screw.Unit; + }, + install: function () { + $(Screw).bind( 'after', function () { + var passed = $( '.passed' ).length, + failed = $( '.failed' ).length; + submit({ + fail: failed, + error: 0, + total: failed + passed + }); + }); + + $( Screw ).bind( 'loaded', function () { + $( '.it' ) + .bind( 'passed', window.TestSwarm.heartbeat ) + .bind( 'failed', window.TestSwarm.heartbeat ); + window.TestSwarm.heartbeat(); + }); + + window.TestSwarm.serialize = function () { + return trimSerialize(); + }; + } + } + }; + + detectAndInstall(); + +}() ); diff --git a/test/logger/index.html b/test/logger/index.html new file mode 100644 index 00000000..57f69f4d --- /dev/null +++ b/test/logger/index.html @@ -0,0 +1,108 @@ + + + + Butter Test Suite [Logger] + + + + + + + + +

                                                                                                Butter API Test Suite [Logger]

                                                                                                +

                                                                                                +
                                                                                                +

                                                                                                +
                                                                                                  + + diff --git a/test/manual-tests/basic-test.js b/test/manual-tests/basic-test.js new file mode 100644 index 00000000..1ad12e68 --- /dev/null +++ b/test/manual-tests/basic-test.js @@ -0,0 +1,39 @@ +document.addEventListener( "DOMContentLoaded", function( e ){ + + Butter.init({ + config: "default-config.json", + ready: function( butter ){ + var media = butter.media[ 0 ]; + + function start(){ + var track = media.addTrack( "Track1" ); + media.addTrack( "Track" + Math.random() ); + media.addTrack( "Track" + Math.random() ); + + var event = track.addTrackEvent({ + type: "text", + popcornOptions: { + start: 0, + end: 3, + text: "test", + target: "Area1" + } + }); + + butter.tracks[ 2 ].addTrackEvent({ + type: "text", + popcornOptions: { + start: 1, + end: 2, + text: "test", + target: "Area2" + } + }); + + } + + media.onReady( start ); + + } + }); //Butter +}, false ); diff --git a/test/manual-tests/default-config.json b/test/manual-tests/default-config.json new file mode 100644 index 00000000..443fa892 --- /dev/null +++ b/test/manual-tests/default-config.json @@ -0,0 +1,12 @@ +{ + "name": "default-butter-test", + "baseDir": "../../", + "cornfield": { + "server": "http://localhost:8888", + "authtype": "browserid" + }, + "ui": { + "trackEventHighlight": "click", + "title": "Popcorn Maker" + } +} diff --git a/test/manual-tests/index.html b/test/manual-tests/index.html new file mode 100644 index 00000000..16d903f7 --- /dev/null +++ b/test/manual-tests/index.html @@ -0,0 +1,126 @@ + + + + Popcorn Maker Manual Tests + + + + + + + +
                                                                                                  + +
                                                                                                  + +
                                                                                                  +

                                                                                                  Manual Tests

                                                                                                  +
                                                                                                    +
                                                                                                    + + + diff --git a/test/manual-tests/manual-test.js b/test/manual-tests/manual-test.js new file mode 100644 index 00000000..d970739e --- /dev/null +++ b/test/manual-tests/manual-test.js @@ -0,0 +1,46 @@ + +(function() { + + document.addEventListener( "DOMContentLoaded", function setup( e ){ + document.removeEventListener( "DOMContentLoaded", setup, false ); + + // Make sure we have a place to stick the Pass / Fail buttons + var buttonDiv = document.getElementById( "manual-test-buttons" ); + if ( !buttonDiv ) { + console.log("Error: couldn't find 'manual-test-buttons' DIV in test page!"); + return; + } + + function getInfo() { + return { + ua: navigator.userAgent, + popcornVersion: Popcorn.version, + butterVersion: Butter.version, + testTitle: document.title, + testURL: /[^\/]+$/.exec(location.pathname)[0] + }; + } + + function passFn() { + parent.postMessage("PASS", "*"); + } + + function failFn() { + parent.postMessage("FAIL", "*"); + } + + function createButton( text, clickFn ) { + var button = document.createElement( "button" ); + button.innerHTML = text; + button.style.cursor = "pointer"; + button.onclick = clickFn; + + return button; + } + + buttonDiv.appendChild( createButton( "PASS", passFn ) ); + buttonDiv.appendChild( createButton( "FAIL", failFn ) ); + + }, false ); + +})(); diff --git a/test/manual-tests/no-events-test.js b/test/manual-tests/no-events-test.js new file mode 100644 index 00000000..8800873c --- /dev/null +++ b/test/manual-tests/no-events-test.js @@ -0,0 +1,19 @@ +document.addEventListener( "DOMContentLoaded", function( e ){ + + Butter.init({ + config: "default-config.json", + ready: function( butter ){ + var media = butter.media[ 0 ]; + + function start(){ + var track = media.addTrack( "Track1" ); + media.addTrack( "Track" + Math.random() ); + media.addTrack( "Track" + Math.random() ); + + } + + media.onReady( start ); + + } + }); //Butter +}, false ); diff --git a/test/manual-tests/test-add.html b/test/manual-tests/test-add.html new file mode 100644 index 00000000..aff51575 --- /dev/null +++ b/test/manual-tests/test-add.html @@ -0,0 +1,63 @@ + + + + Manual Test: Change Media to YouTube + + + + + + + + + +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 1

                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 2

                                                                                                    +
                                                                                                    +
                                                                                                    + + +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Manual Test: Add a popcorn event

                                                                                                    +

                                                                                                    This test makes sure that popcorn events can be added without incident.

                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Steps to Follow:

                                                                                                    +
                                                                                                      +
                                                                                                    1. Wait for page to load, make sure HTML5 video is displayed on page +
                                                                                                    2. Drag the "text" plugin from My Events (lower right) into the "area 1" div +
                                                                                                    3. In the dialogue that appears in the tray, enter arbitrary text +
                                                                                                    4. Click "done" to create the event +
                                                                                                    5. Drag the "image" plugin from My Events (lower right) into the "area 2" div +
                                                                                                    6. In the dialogue that appears in the tray, paste this URL http://mozillapopcorn.org/wp-content/uploads/2011/11/icon_popcorn_js-150x150.png into the source URL field +
                                                                                                    7. Click "done" to create the event +
                                                                                                    8. Scroll the playhead to a time before the events start +
                                                                                                    9. Play through +
                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Expected Result

                                                                                                    +

                                                                                                    DIV 1 should have the text you entered, DIV 2 should display an image

                                                                                                    +
                                                                                                    + + +
                                                                                                    +
                                                                                                    + + + diff --git a/test/manual-tests/test-delete.html b/test/manual-tests/test-delete.html new file mode 100644 index 00000000..fb87a0de --- /dev/null +++ b/test/manual-tests/test-delete.html @@ -0,0 +1,60 @@ + + + + Manual Test: Change Media to YouTube + + + + + + + + + +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 1

                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 2

                                                                                                    +
                                                                                                    +
                                                                                                    + + +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Manual Test: Delete a popcorn event

                                                                                                    +

                                                                                                    This test makes sure that popcorn events can be deleted without incident.

                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Steps to Follow:

                                                                                                    +
                                                                                                      +
                                                                                                    1. Wait for page to load, make sure events appear on the timeline +
                                                                                                    2. Click on event in timeline to select it +
                                                                                                    3. Press delete key on keyboard +
                                                                                                    4. Repeat for all events +
                                                                                                    5. Scroll the playhead to a time before the event previously existed +
                                                                                                    6. Play through +
                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Expected Result

                                                                                                    +

                                                                                                    Pressing delete key should remove the event from the timeline - no events should appear in target divs on the page

                                                                                                    +
                                                                                                    + + +
                                                                                                    +
                                                                                                    + + + diff --git a/test/manual-tests/test-export.html b/test/manual-tests/test-export.html new file mode 100644 index 00000000..9d5b4599 --- /dev/null +++ b/test/manual-tests/test-export.html @@ -0,0 +1,70 @@ + + + + Manual Test: Export HTML5 video + + + + + + + + + +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 1

                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 2

                                                                                                    +
                                                                                                    +
                                                                                                    + + +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Manual Test: Export HTML5 video

                                                                                                    +

                                                                                                    This test makes sure that standard <video> media can be exported for external hosting

                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Steps to Follow:

                                                                                                    +
                                                                                                      +
                                                                                                    1. Wait for page to load, make sure HTML5 video is displayed on page +
                                                                                                    2. Drag the "text" plugin from My Events (lower right) into the "area 1" div +
                                                                                                    3. In the dialogue that appears in the tray, enter arbitrary text +
                                                                                                    4. Click "done" to create the event +
                                                                                                    5. Drag the "image" plugin from My Events (lower right) into the "area 2" div +
                                                                                                    6. In the dialogue that appears in the tray, paste this URL http://mozillapopcorn.org/wp-content/uploads/2011/11/icon_popcorn_js-150x150.png into the source URL field +
                                                                                                    7. Log in via browser id (click the login/sign up button in top right corner) +
                                                                                                    8. When prompted, give the project a name +
                                                                                                    9. Click the "export" button +
                                                                                                    10. Copy the provided HTML +
                                                                                                    11. Save as .html file and upload to your test server +
                                                                                                    12. Watch to make sure it matches your work +
                                                                                                    13. Delete the image event +
                                                                                                    14. Click the export button again, copy and save again to test server +
                                                                                                    15. Watch to make sure this event has dissapeared +
                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Expected Result

                                                                                                    +

                                                                                                    You should be able to log in to browser ID, save your work, and export it. The exported work should exactly match what you created in Butter, including image paths and styles. When you delete one of the events, they should no longer appear in the new export. +

                                                                                                    +
                                                                                                    + + +
                                                                                                    +
                                                                                                    + + + diff --git a/test/manual-tests/test-save.html b/test/manual-tests/test-save.html new file mode 100644 index 00000000..e0abb5cb --- /dev/null +++ b/test/manual-tests/test-save.html @@ -0,0 +1,67 @@ + + + + Manual Test: Save and restore a project + + + + + + + + + +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 1

                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 2

                                                                                                    +
                                                                                                    +
                                                                                                    + + +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Manual Test: Save and restore a project

                                                                                                    +

                                                                                                    This test makes sure that projects can be saved and restored

                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Steps to Follow:

                                                                                                    +
                                                                                                      +
                                                                                                    1. Wait for page to load, make sure HTML5 video is displayed on page +
                                                                                                    2. Drag the "text" plugin from My Events (lower right) into the "area 1" div +
                                                                                                    3. In the dialogue that appears in the tray, enter arbitrary text +
                                                                                                    4. Click "done" to create the event +
                                                                                                    5. Drag the "image" plugin from My Events (lower right) into the "area 2" div +
                                                                                                    6. In the dialogue that appears in the tray, paste this URL http://mozillapopcorn.org/wp-content/uploads/2011/11/icon_popcorn_js-150x150.png into the source URL field +
                                                                                                    7. Log in via browser id (click the login/sign up button in top right corner) +
                                                                                                    8. Click the "save" button +
                                                                                                    9. Give the project a name +
                                                                                                    10. Delete the image event +
                                                                                                    11. Click the "load" button +
                                                                                                    12. Watch to make sure the event re-appears +
                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Expected Result

                                                                                                    +

                                                                                                    You should be able to log in to browser ID, save your work, and restore it. +

                                                                                                    +
                                                                                                    + + +
                                                                                                    +
                                                                                                    + + + diff --git a/test/manual-tests/test-share.html b/test/manual-tests/test-share.html new file mode 100644 index 00000000..0b8c2a42 --- /dev/null +++ b/test/manual-tests/test-share.html @@ -0,0 +1,69 @@ + + + + Manual Test: Share a project + + + + + + + + + +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 1

                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 2

                                                                                                    +
                                                                                                    +
                                                                                                    + + +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Manual Test: Share a project w/ HTML5 video

                                                                                                    +

                                                                                                    This test makes sure that standard <video> media can be shared.

                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Steps to Follow:

                                                                                                    +
                                                                                                      +
                                                                                                    1. Wait for page to load, make sure HTML5 video is displayed on page +
                                                                                                    2. Drag the "text" plugin from My Events (lower right) into the "area 1" div +
                                                                                                    3. In the dialogue that appears in the tray, enter arbitrary text +
                                                                                                    4. Click "done" to create the event +
                                                                                                    5. Drag the "image" plugin from My Events (lower right) into the "area 2" div +
                                                                                                    6. In the dialogue that appears in the tray, paste this URL http://mozillapopcorn.org/wp-content/uploads/2011/11/icon_popcorn_js-150x150.png into the source URL field +
                                                                                                    7. Log in via browser id (click the login/sign up button in top right corner) +
                                                                                                    8. When prompted, give the project a name +
                                                                                                    9. Click the "share" button +
                                                                                                    10. Visit the provided link +
                                                                                                    11. Watch to make sure it matches your work +
                                                                                                    12. Delete the image event +
                                                                                                    13. Click the share button again +
                                                                                                    14. Watch to make sure this event has dissapeared +
                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Expected Result

                                                                                                    +

                                                                                                    You should be able to log in to browser ID, save your work, and share it. The shared work should exactly match what you created in Butter, including image paths and styles. When you delete one of the events, they should no longer appear in the new export. +

                                                                                                    +
                                                                                                    + + +
                                                                                                    +
                                                                                                    + + + diff --git a/test/manual-tests/test-sharevimeo.html b/test/manual-tests/test-sharevimeo.html new file mode 100644 index 00000000..3b7034e6 --- /dev/null +++ b/test/manual-tests/test-sharevimeo.html @@ -0,0 +1,73 @@ + + + + Manual Test: Share a Vimeo project + + + + + + + + + +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 1

                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 2

                                                                                                    +
                                                                                                    +
                                                                                                    + + +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Manual Test: Share a project w/ Vimeo video

                                                                                                    +

                                                                                                    This test makes sure that projects using Vimeo media can be shared.

                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Steps to Follow:

                                                                                                    +
                                                                                                      +
                                                                                                    1. Wait for page to load, make sure HTML5 video is displayed on page +
                                                                                                    2. Hover mouse over "Edit source..." area on HTML5 video (top-left corner) +
                                                                                                    3. Enter the following Vimeo URL: http://vimeo.com/25107077 +
                                                                                                    4. Click the "Save" button in the source editor +
                                                                                                    5. Wait for the Vimeo video to load +
                                                                                                    6. Drag the "text" plugin from My Events (lower right) into the "area 1" div +
                                                                                                    7. In the dialogue that appears in the tray, enter arbitrary text +
                                                                                                    8. Click "done" to create the event +
                                                                                                    9. Drag the "image" plugin from My Events (lower right) into the "area 2" div +
                                                                                                    10. In the dialogue that appears in the tray, paste this URL http://mozillapopcorn.org/wp-content/uploads/2011/11/icon_popcorn_js-150x150.png into the source URL field +
                                                                                                    11. Log in via browser id (click the login/sign up button in top right corner) +
                                                                                                    12. When prompted, give the project a name +
                                                                                                    13. Click the "share" button +
                                                                                                    14. Visit the provided link +
                                                                                                    15. Watch to make sure it matches your work +
                                                                                                    16. Delete the image event +
                                                                                                    17. Click the share button again +
                                                                                                    18. Watch to make sure this event has dissapeared +
                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Expected Result

                                                                                                    +

                                                                                                    You should be able to change the media to a Vimeo URL, log in to browser ID, save your work, and share it. The shared work should exactly match what you created in Butter, including image paths and styles. When you delete one of the events, they should no longer appear in the new export. +

                                                                                                    +
                                                                                                    + + +
                                                                                                    +
                                                                                                    + + + diff --git a/test/manual-tests/test-shareyoutube.html b/test/manual-tests/test-shareyoutube.html new file mode 100644 index 00000000..7dd846fa --- /dev/null +++ b/test/manual-tests/test-shareyoutube.html @@ -0,0 +1,73 @@ + + + + Manual Test: Share a Youtube project + + + + + + + + + +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 1

                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 2

                                                                                                    +
                                                                                                    +
                                                                                                    + + +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Manual Test: Share a project w/ Youtube video

                                                                                                    +

                                                                                                    This test makes sure that projects using Youtube media can be shared.

                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Steps to Follow:

                                                                                                    +
                                                                                                      +
                                                                                                    1. Wait for page to load, make sure HTML5 video is displayed on page +
                                                                                                    2. Hover mouse over "Edit source..." area on HTML5 video (top-left corner) +
                                                                                                    3. Enter the followoing YouTube URL: http://www.youtube.com/watch?v=CxvgCLgwdNk +
                                                                                                    4. Click the "Save" button in the source editor +
                                                                                                    5. Wait for the YouTube video to load +
                                                                                                    6. Drag the "text" plugin from My Events (lower right) into the "area 1" div +
                                                                                                    7. In the dialogue that appears in the tray, enter arbitrary text +
                                                                                                    8. Click "done" to create the event +
                                                                                                    9. Drag the "image" plugin from My Events (lower right) into the "area 2" div +
                                                                                                    10. In the dialogue that appears in the tray, paste this URL http://mozillapopcorn.org/wp-content/uploads/2011/11/icon_popcorn_js-150x150.png into the source URL field +
                                                                                                    11. Log in via browser id (click the login/sign up button in top right corner) +
                                                                                                    12. When prompted, give the project a name +
                                                                                                    13. Click the "share" button +
                                                                                                    14. Visit the provided link +
                                                                                                    15. Watch to make sure it matches your work +
                                                                                                    16. Delete the image event +
                                                                                                    17. Click the share button again +
                                                                                                    18. W atch to make sure this event has dissapeared +
                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Expected Result

                                                                                                    +

                                                                                                    You should be able to change the media to a youtube URL, log in to browser ID, save your work, and share it. The shared work should exactly match what you created in Butter, including image paths and styles. When you delete one of the events, they should no longer appear in the new export. +

                                                                                                    +
                                                                                                    + + +
                                                                                                    +
                                                                                                    + + + diff --git a/test/manual-tests/test-tracks.html b/test/manual-tests/test-tracks.html new file mode 100644 index 00000000..1ba88b8f --- /dev/null +++ b/test/manual-tests/test-tracks.html @@ -0,0 +1,59 @@ + + + + Manual Test: Create new Tracks + + + + + + + + + +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 1

                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 2

                                                                                                    +
                                                                                                    +
                                                                                                    + + +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Manual Test: Create new Tracks

                                                                                                    +

                                                                                                    This test makes sure that users can add tracks and move events between tracks without incident.

                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Steps to Follow:

                                                                                                    +
                                                                                                      +
                                                                                                    1. Wait for page to load, make sure HTML5 video is displayed on page +
                                                                                                    2. Click the red "+track" button on the lower left hand side of the tray +
                                                                                                    3. Move an existing event from its current track to the new track +
                                                                                                    4. Scroll the playhead to a time before the events start +
                                                                                                    5. Play through +
                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Expected Result

                                                                                                    +

                                                                                                    A new layer should be created, and the event should still fire in it's exptect target while on a new layer

                                                                                                    +
                                                                                                    + + +
                                                                                                    +
                                                                                                    + + + diff --git a/test/manual-tests/test-vimeo.html b/test/manual-tests/test-vimeo.html new file mode 100644 index 00000000..43fa4ef0 --- /dev/null +++ b/test/manual-tests/test-vimeo.html @@ -0,0 +1,62 @@ + + + + Manual Test: Change Media to Vimeo + + + + + + + + + +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 1

                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 2

                                                                                                    +
                                                                                                    +
                                                                                                    + + +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Manual Test: Change Media to Vimeo

                                                                                                    +

                                                                                                    This test makes sure that swapping a Vimeo video for an existing HTML5 video + in a project works, and maintains the existing track events.

                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Steps to Follow:

                                                                                                    +
                                                                                                      +
                                                                                                    1. Wait for page to load, make sure HTML5 video is displayed on page +
                                                                                                    2. Hover mouse over "Edit source..." area on HTML5 video (top-left corner) +
                                                                                                    3. Enter the followoing Vimeo URL: http://vimeo.com/25107077 +
                                                                                                    4. Click the "Save" button in the source editor +
                                                                                                    5. Wait for the Vimeo video to load +
                                                                                                    6. Play the video and observe Area 1 and Area 2 +
                                                                                                    7. Drag the playhead along the timeline +
                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Expected Result

                                                                                                    +

                                                                                                    The HTML5 video should have been changed to a Vimeo video, and track events should fire in the Area 1 and Area 2 DIVs. When dragging the playhead along the timeline, the timeline media should update.

                                                                                                    +
                                                                                                    + + +
                                                                                                    +
                                                                                                    + + + diff --git a/test/manual-tests/test-youtube.html b/test/manual-tests/test-youtube.html new file mode 100644 index 00000000..5242a7e7 --- /dev/null +++ b/test/manual-tests/test-youtube.html @@ -0,0 +1,61 @@ + + + + Manual Test: Change Media to YouTube + + + + + + + + + +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 1

                                                                                                    +
                                                                                                    +
                                                                                                    +
                                                                                                    +

                                                                                                    Area 2

                                                                                                    +
                                                                                                    +
                                                                                                    + + +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Manual Test: Change Media to YouTube

                                                                                                    +

                                                                                                    This test makes sure that swapping a YouTube video for an existing HTML5 video + in a project works, and maintains the existing track events.

                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Steps to Follow:

                                                                                                    +
                                                                                                      +
                                                                                                    1. Wait for page to load, make sure HTML5 video is displayed on page +
                                                                                                    2. Hover mouse over "Edit source..." area on HTML5 video (top-left corner) +
                                                                                                    3. Enter the followoing YouTube URL: http://www.youtube.com/watch?v=CxvgCLgwdNk +
                                                                                                    4. Click the "Save" button in the source editor +
                                                                                                    5. Wait for the YouTube video to load +
                                                                                                    6. Play the video and observe Area 1 and Area 2 +
                                                                                                    +
                                                                                                    + + +
                                                                                                    +

                                                                                                    Expected Result

                                                                                                    +

                                                                                                    The HTML5 video should have been changed to a YouTube video, and track events should fire in the Area 1 and Area 2 DIVs

                                                                                                    +
                                                                                                    + + +
                                                                                                    +
                                                                                                    + + + diff --git a/test/manual-tests/tests.js b/test/manual-tests/tests.js new file mode 100644 index 00000000..dc96b82d --- /dev/null +++ b/test/manual-tests/tests.js @@ -0,0 +1,15 @@ +/** + * Add your manual test filenames and display names below. + **/ +var tests = [ + { href: "test-add.html", name: "Add Events" }, + { href: "test-delete.html", name: "Delete Events" }, + { href: "test-tracks.html", name: "Add Tracks" }, + { href: "test-vimeo.html", name: "Change Media to Vimeo" }, + { href: "test-youtube.html", name: "Change Media to Youtube" }, + { href: "test-save.html", name: "Save and Load" }, + { href: "test-share.html", name: "Share HTML5 project" }, + { href: "test-sharevimeo.html", name: "Share Vimeo project" }, + { href: "test-shareyoutube.html", name: "Share Youtube project" }, + { href: "test-export.html", name: "Export HTML5 project" } +].reverse(); diff --git a/test/media/index.html b/test/media/index.html new file mode 100644 index 00000000..d7b83fef --- /dev/null +++ b/test/media/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                                                                                                    + Butter Test Suite [ Media Module ] + +

                                                                                                    +

                                                                                                    +

                                                                                                    +
                                                                                                      + + diff --git a/test/media/media-addTrack.html b/test/media/media-addTrack.html new file mode 100644 index 00000000..4c9a9088 --- /dev/null +++ b/test/media/media-addTrack.html @@ -0,0 +1,34 @@ + + + + Butter Test Suite [Media Module] + + + + + + + + + +

                                                                                                      Butter API Test Suite [Media Module]

                                                                                                      +

                                                                                                      +
                                                                                                      +

                                                                                                      +
                                                                                                        + + diff --git a/test/media/media-findTrackWithTrackEventId.html b/test/media/media-findTrackWithTrackEventId.html new file mode 100644 index 00000000..93705ae7 --- /dev/null +++ b/test/media/media-findTrackWithTrackEventId.html @@ -0,0 +1,36 @@ + + + + Butter Test Suite [Media Module] + + + + + + + + + +

                                                                                                        Butter API Test Suite [Media Module]

                                                                                                        +

                                                                                                        +
                                                                                                        +

                                                                                                        +
                                                                                                          + + diff --git a/test/media/media-generatePopcornString.html b/test/media/media-generatePopcornString.html new file mode 100644 index 00000000..8c674c9a --- /dev/null +++ b/test/media/media-generatePopcornString.html @@ -0,0 +1,32 @@ + + + + Butter Test Suite [Media Module] + + + + + + + + + +

                                                                                                          Butter API Test Suite [Media Module]

                                                                                                          +

                                                                                                          +
                                                                                                          +

                                                                                                          +
                                                                                                            + + diff --git a/test/media/media-getManifest.html b/test/media/media-getManifest.html new file mode 100644 index 00000000..e233774e --- /dev/null +++ b/test/media/media-getManifest.html @@ -0,0 +1,38 @@ + + + + Butter Test Suite [Media Module] + + + + + + + + + +

                                                                                                            Butter API Test Suite [Media Module]

                                                                                                            +

                                                                                                            +
                                                                                                            +

                                                                                                            +
                                                                                                              + + diff --git a/test/media/media-getTrackById.html b/test/media/media-getTrackById.html new file mode 100644 index 00000000..1691d9c9 --- /dev/null +++ b/test/media/media-getTrackById.html @@ -0,0 +1,30 @@ + + + + Butter Test Suite [Media Module] + + + + + + + + + +

                                                                                                              Butter API Test Suite [Media Module]

                                                                                                              +

                                                                                                              +
                                                                                                              +

                                                                                                              +
                                                                                                                + + diff --git a/test/media/media-removeTrack.html b/test/media/media-removeTrack.html new file mode 100644 index 00000000..2013a8e2 --- /dev/null +++ b/test/media/media-removeTrack.html @@ -0,0 +1,45 @@ + + + + Butter Test Suite [Media Module] + + + + + + + + + +

                                                                                                                Butter API Test Suite [Media Module]

                                                                                                                +

                                                                                                                +
                                                                                                                +

                                                                                                                +
                                                                                                                  + + diff --git a/test/media/media-sortTracks.html b/test/media/media-sortTracks.html new file mode 100644 index 00000000..aee4fa98 --- /dev/null +++ b/test/media/media-sortTracks.html @@ -0,0 +1,83 @@ + + + + Butter Test Suite [Media Module] + + + + + + + + + +

                                                                                                                  Butter API Test Suite [Media Module]

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +

                                                                                                                  +
                                                                                                                    + + diff --git a/test/media/media-tests.conf b/test/media/media-tests.conf new file mode 100644 index 00000000..628bb764 --- /dev/null +++ b/test/media/media-tests.conf @@ -0,0 +1,11 @@ +{ + "media": { + "addTrack": "test/media/media-addTrack.html", + "findTrackWithTrackEventId": "test/media/media-findTrackWithTrackEventId.html", + "generatePopcornString": "test/media/media-generatePopcornString.html", + "getManifest": "test/media/media-getManifest.html", + "getTrackById": "test/media/media-getTrackById.html", + "removeTrack": "test/media/media-removeTrack.html", + "sortTracks": "test/media/media-sortTracks.html" + } +} \ No newline at end of file diff --git a/test/observer/index.html b/test/observer/index.html new file mode 100644 index 00000000..c947f6d8 --- /dev/null +++ b/test/observer/index.html @@ -0,0 +1,106 @@ + + + + Butter Test Suite [Observer] + + + + + \ + + + +

                                                                                                                    Butter API Test Suite [EventManagerWrapper]

                                                                                                                    +

                                                                                                                    +
                                                                                                                    +

                                                                                                                    +
                                                                                                                      + + diff --git a/test/page/index.html b/test/page/index.html new file mode 100644 index 00000000..30ca58ac --- /dev/null +++ b/test/page/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                                                                                                                      + Butter Test Suite [ Page Module ] + +

                                                                                                                      +

                                                                                                                      +

                                                                                                                      +
                                                                                                                        + + diff --git a/test/page/page-addPlayerType.html b/test/page/page-addPlayerType.html new file mode 100644 index 00000000..37577847 --- /dev/null +++ b/test/page/page-addPlayerType.html @@ -0,0 +1,33 @@ + + + + Butter Test Suite [Page] + + + + + + + + + + +

                                                                                                                        Butter API Test Suite [Page]

                                                                                                                        +

                                                                                                                        +
                                                                                                                        +

                                                                                                                        +
                                                                                                                          +
                                                                                                                          + + diff --git a/test/page/page-prepare.html b/test/page/page-prepare.html new file mode 100644 index 00000000..dd1955c3 --- /dev/null +++ b/test/page/page-prepare.html @@ -0,0 +1,51 @@ + + + + Butter Test Suite [Page Module] + + + + + + + + + +

                                                                                                                          Butter API Test Suite [Page Module]

                                                                                                                          +

                                                                                                                          +
                                                                                                                          +

                                                                                                                          +
                                                                                                                            + + diff --git a/test/page/page-scrape.html b/test/page/page-scrape.html new file mode 100644 index 00000000..2ad6928c --- /dev/null +++ b/test/page/page-scrape.html @@ -0,0 +1,58 @@ + + + + Butter Test Suite [Page] + + + + + + + + + + +

                                                                                                                            Butter API Test Suite [Page]

                                                                                                                            +

                                                                                                                            +
                                                                                                                            +

                                                                                                                            +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +

                                                                                                                              Area 1

                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +

                                                                                                                              Area 2

                                                                                                                              +
                                                                                                                              +
                                                                                                                              + + diff --git a/test/page/page-tests.conf b/test/page/page-tests.conf new file mode 100644 index 00000000..ff144632 --- /dev/null +++ b/test/page/page-tests.conf @@ -0,0 +1,7 @@ +{ + "page": { + "prepare": "test/page/page-prepare.html", + "addPlayerType": "test/page/page-addPlayerType.html", + "scrape": "test/page/page-scrape.html" + } +} \ No newline at end of file diff --git a/test/page/saved-data.json b/test/page/saved-data.json new file mode 100644 index 00000000..bc0afdc6 --- /dev/null +++ b/test/page/saved-data.json @@ -0,0 +1,64 @@ +{ + "targets": [ + { + "id": "Target0", + "name": "Target0", + "element": "Area1" + }, + { + "id": "Target1", + "name": "Target1", + "element": "Area2" + } + ], + "media": [ + { + "id": "Media0", + "name": "Media0", + "url": "http://www.youtube.com/watch?v=31g0YE61PLQ", + "target": "main", + "tracks": [ + { + "name": "Track0", + "id": "Track0", + "trackEvents": [ + { + "id": "TrackEvent0", + "type": "text", + "popcornOptions": { + "start": 0, + "end": 3, + "text": "test", + "target": "Area1" + }, + "track": "Track0", + "name": "TrackEvent0" + } + ] + }, + { + "name": "Track1", + "id": "Track1", + "trackEvents": [] + }, + { + "name": "Track2", + "id": "Track2", + "trackEvents": [ + { + "id": "TrackEvent1", + "type": "text", + "popcornOptions": { + "start": 1, + "end": 2, + "target": "Area2" + }, + "track": "Track2", + "name": "TrackEvent1" + } + ] + } + ] + } + ] +} diff --git a/test/page/test-config.json b/test/page/test-config.json new file mode 100644 index 00000000..650a26b7 --- /dev/null +++ b/test/page/test-config.json @@ -0,0 +1,9 @@ +{ + "name": "pageTest", + "savedDataUrl": "saved-data.json", + "baseDir": "../../", + "makeVideoURLsUnique": false, + "ui": { + "enabled": false + } +} diff --git a/test/plugins/index.html b/test/plugins/index.html new file mode 100644 index 00000000..f0cb7ff2 --- /dev/null +++ b/test/plugins/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                                                                                                                              + Butter Test Suite [ Page Module ] + +

                                                                                                                              +

                                                                                                                              +

                                                                                                                              +
                                                                                                                                + + diff --git a/test/plugins/plugins-add.html b/test/plugins/plugins-add.html new file mode 100644 index 00000000..7ed6a10f --- /dev/null +++ b/test/plugins/plugins-add.html @@ -0,0 +1,31 @@ + + + + Butter Test Suite [Plugins] + + + + + + + + + +

                                                                                                                                Butter API Test Suite [Plugins]

                                                                                                                                +

                                                                                                                                +
                                                                                                                                +

                                                                                                                                +
                                                                                                                                  +
                                                                                                                                  +
                                                                                                                                  +
                                                                                                                                  + + diff --git a/test/plugins/plugins-createElement.html b/test/plugins/plugins-createElement.html new file mode 100644 index 00000000..aa330bb4 --- /dev/null +++ b/test/plugins/plugins-createElement.html @@ -0,0 +1,41 @@ + + + + Butter Test Suite [Plugins] + + + + + + + + + +

                                                                                                                                  Butter API Test Suite [Plugins]

                                                                                                                                  +

                                                                                                                                  +
                                                                                                                                  +

                                                                                                                                  +
                                                                                                                                    +
                                                                                                                                    +
                                                                                                                                    +
                                                                                                                                    + + diff --git a/test/plugins/plugins-remove.html b/test/plugins/plugins-remove.html new file mode 100644 index 00000000..eeb79bee --- /dev/null +++ b/test/plugins/plugins-remove.html @@ -0,0 +1,45 @@ + + + + Butter Test Suite [Plugins] + + + + + + + + + +

                                                                                                                                    Butter API Test Suite [Plugins]

                                                                                                                                    +

                                                                                                                                    +
                                                                                                                                    +

                                                                                                                                    +
                                                                                                                                      +
                                                                                                                                      +
                                                                                                                                      +
                                                                                                                                      + + diff --git a/test/plugins/plugins-tests.conf b/test/plugins/plugins-tests.conf new file mode 100644 index 00000000..f7137d90 --- /dev/null +++ b/test/plugins/plugins-tests.conf @@ -0,0 +1,7 @@ +{ + "plugins": { + "add": "test/plugins/plugins-add.html", + "remove": "test/plugins/plugins-remove.html", + "createElement": "test/plugins/plugins-createElement.html" + } +} \ No newline at end of file diff --git a/test/popcorn-wrapper/index.html b/test/popcorn-wrapper/index.html new file mode 100644 index 00000000..db4748ca --- /dev/null +++ b/test/popcorn-wrapper/index.html @@ -0,0 +1,169 @@ + + + + Butter Test Suite [Popcorn Wrapper] + + + + + + + + + + + + + + + + + +

                                                                                                                                      Butter API Test Suite [Popcorn Wrapper]

                                                                                                                                      +

                                                                                                                                      +
                                                                                                                                      +

                                                                                                                                      +
                                                                                                                                        +
                                                                                                                                        +
                                                                                                                                        +
                                                                                                                                        + + diff --git a/test/qunit/qunit.css b/test/qunit/qunit.css new file mode 100644 index 00000000..b3c6db52 --- /dev/null +++ b/test/qunit/qunit.css @@ -0,0 +1,225 @@ +/** + * QUnit - A JavaScript Unit Testing Framework + * + * http://docs.jquery.com/QUnit + * + * Copyright (c) 2011 John Resig, Jörn Zaefferer + * Dual licensed under the MIT (MIT-LICENSE.txt) + * or GPL (GPL-LICENSE.txt) licenses. + */ + +/** Font Family and Sizes */ + +#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { + font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; +} + +#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } +#qunit-tests { font-size: smaller; } + + +/** Resets */ + +#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { + margin: 0; + padding: 0; +} + + +/** Header */ + +#qunit-header { + padding: 0.5em 0 0.5em 1em; + + color: #8699a4; + background-color: #0d3349; + + font-size: 1.5em; + line-height: 1em; + font-weight: normal; + + border-radius: 15px 15px 0 0; + -moz-border-radius: 15px 15px 0 0; + -webkit-border-top-right-radius: 15px; + -webkit-border-top-left-radius: 15px; +} + +#qunit-header a { + text-decoration: none; + color: #c2ccd1; +} + +#qunit-header a:hover, +#qunit-header a:focus { + color: #fff; +} + +#qunit-banner { + height: 5px; +} + +#qunit-testrunner-toolbar { + padding: 0.5em 0 0.5em 2em; + color: #5E740B; + background-color: #eee; +} + +#qunit-userAgent { + padding: 0.5em 0 0.5em 2.5em; + background-color: #2b81af; + color: #fff; + text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; +} + + +/** Tests: Pass/Fail */ + +#qunit-tests { + list-style-position: inside; +} + +#qunit-tests li { + padding: 0.4em 0.5em 0.4em 2.5em; + border-bottom: 1px solid #fff; + list-style-position: inside; +} + +#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { + display: none; +} + +#qunit-tests li strong { + cursor: pointer; +} + +#qunit-tests li a { + padding: 0.5em; + color: #c2ccd1; + text-decoration: none; +} +#qunit-tests li a:hover, +#qunit-tests li a:focus { + color: #000; +} + +#qunit-tests ol { + margin-top: 0.5em; + padding: 0.5em; + + background-color: #fff; + + border-radius: 15px; + -moz-border-radius: 15px; + -webkit-border-radius: 15px; + + box-shadow: inset 0px 2px 13px #999; + -moz-box-shadow: inset 0px 2px 13px #999; + -webkit-box-shadow: inset 0px 2px 13px #999; +} + +#qunit-tests table { + border-collapse: collapse; + margin-top: .2em; +} + +#qunit-tests th { + text-align: right; + vertical-align: top; + padding: 0 .5em 0 0; +} + +#qunit-tests td { + vertical-align: top; +} + +#qunit-tests pre { + margin: 0; + white-space: pre-wrap; + word-wrap: break-word; +} + +#qunit-tests del { + background-color: #e0f2be; + color: #374e0c; + text-decoration: none; +} + +#qunit-tests ins { + background-color: #ffcaca; + color: #500; + text-decoration: none; +} + +/*** Test Counts */ + +#qunit-tests b.counts { color: black; } +#qunit-tests b.passed { color: #5E740B; } +#qunit-tests b.failed { color: #710909; } + +#qunit-tests li li { + margin: 0.5em; + padding: 0.4em 0.5em 0.4em 0.5em; + background-color: #fff; + border-bottom: none; + list-style-position: inside; +} + +/*** Passing Styles */ + +#qunit-tests li li.pass { + color: #5E740B; + background-color: #fff; + border-left: 26px solid #C6E746; +} + +#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } +#qunit-tests .pass .test-name { color: #366097; } + +#qunit-tests .pass .test-actual, +#qunit-tests .pass .test-expected { color: #999999; } + +#qunit-banner.qunit-pass { background-color: #C6E746; } + +/*** Failing Styles */ + +#qunit-tests li li.fail { + color: #710909; + background-color: #fff; + border-left: 26px solid #EE5757; +} + +#qunit-tests > li:last-child { + border-radius: 0 0 15px 15px; + -moz-border-radius: 0 0 15px 15px; + -webkit-border-bottom-right-radius: 15px; + -webkit-border-bottom-left-radius: 15px; +} + +#qunit-tests .fail { color: #000000; background-color: #EE5757; } +#qunit-tests .fail .test-name, +#qunit-tests .fail .module-name { color: #000000; } + +#qunit-tests .fail .test-actual { color: #EE5757; } +#qunit-tests .fail .test-expected { color: green; } + +#qunit-banner.qunit-fail { background-color: #EE5757; } + + +/** Result */ + +#qunit-testresult { + padding: 0.5em 0.5em 0.5em 2.5em; + + color: #2b81af; + background-color: #D2E0E6; + + border-bottom: 1px solid white; +} + +/** Fixture */ + +#qunit-fixture { + position: absolute; + top: -10000px; + left: -10000px; +} diff --git a/test/qunit/qunit.js b/test/qunit/qunit.js new file mode 100644 index 00000000..e00cca90 --- /dev/null +++ b/test/qunit/qunit.js @@ -0,0 +1,1448 @@ +/** + * QUnit - A JavaScript Unit Testing Framework + * + * http://docs.jquery.com/QUnit + * + * Copyright (c) 2011 John Resig, Jörn Zaefferer + * Dual licensed under the MIT (MIT-LICENSE.txt) + * or GPL (GPL-LICENSE.txt) licenses. + */ + +(function(window) { + +var defined = { + setTimeout: typeof window.setTimeout !== "undefined", + sessionStorage: (function() { + try { + return !!sessionStorage.getItem; + } catch(e){ + return false; + } + })() +}; + +var testId = 0; + +var Test = function(name, testName, expected, testEnvironmentArg, async, callback) { + this.name = name; + this.testName = testName; + this.expected = expected; + this.testEnvironmentArg = testEnvironmentArg; + this.async = async; + this.callback = callback; + this.assertions = []; +}; +Test.prototype = { + init: function() { + var tests = id("qunit-tests"); + if (tests) { + var b = document.createElement("strong"); + b.innerHTML = "Running " + this.name; + var li = document.createElement("li"); + li.appendChild( b ); + li.className = "running"; + li.id = this.id = "test-output" + testId++; + tests.appendChild( li ); + } + }, + setup: function() { + if (this.module != config.previousModule) { + if ( config.previousModule ) { + QUnit.moduleDone( { + name: config.previousModule, + failed: config.moduleStats.bad, + passed: config.moduleStats.all - config.moduleStats.bad, + total: config.moduleStats.all + } ); + } + config.previousModule = this.module; + config.moduleStats = { all: 0, bad: 0 }; + QUnit.moduleStart( { + name: this.module + } ); + } + + config.current = this; + this.testEnvironment = extend({ + setup: function() {}, + teardown: function() {} + }, this.moduleTestEnvironment); + if (this.testEnvironmentArg) { + extend(this.testEnvironment, this.testEnvironmentArg); + } + + QUnit.testStart( { + name: this.testName + } ); + + // allow utility functions to access the current test environment + // TODO why?? + QUnit.current_testEnvironment = this.testEnvironment; + + try { + if ( !config.pollution ) { + saveGlobal(); + } + + this.testEnvironment.setup.call(this.testEnvironment); + } catch(e) { + QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message ); + } + }, + run: function() { + if ( this.async ) { + QUnit.stop(); + } + + if ( config.notrycatch ) { + this.callback.call(this.testEnvironment); + return; + } + try { + this.callback.call(this.testEnvironment); + } catch(e) { + fail("Test " + this.testName + " died, exception and test follows", e, this.callback); + QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) ); + // else next test will carry the responsibility + saveGlobal(); + + // Restart the tests if they're blocking + if ( config.blocking ) { + start(); + } + } + }, + teardown: function() { + try { + this.testEnvironment.teardown.call(this.testEnvironment); + checkPollution(); + } catch(e) { + QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message ); + } + }, + finish: function() { + if ( this.expected && this.expected != this.assertions.length ) { + QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); + } + + var good = 0, bad = 0, + tests = id("qunit-tests"); + + config.stats.all += this.assertions.length; + config.moduleStats.all += this.assertions.length; + + if ( tests ) { + var ol = document.createElement("ol"); + + for ( var i = 0; i < this.assertions.length; i++ ) { + var assertion = this.assertions[i]; + + var li = document.createElement("li"); + li.className = assertion.result ? "pass" : "fail"; + li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed"); + ol.appendChild( li ); + + if ( assertion.result ) { + good++; + } else { + bad++; + config.stats.bad++; + config.moduleStats.bad++; + } + } + + // store result when possible + if ( QUnit.config.reorder && defined.sessionStorage ) { + if (bad) { + sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad); + } else { + sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName); + } + } + + if (bad == 0) { + ol.style.display = "none"; + } + + var b = document.createElement("strong"); + b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; + + var a = document.createElement("a"); + a.innerHTML = "Rerun"; + a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); + + addEvent(b, "click", function() { + var next = b.nextSibling.nextSibling, + display = next.style.display; + next.style.display = display === "none" ? "block" : "none"; + }); + + addEvent(b, "dblclick", function(e) { + var target = e && e.target ? e.target : window.event.srcElement; + if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { + target = target.parentNode; + } + if ( window.location && target.nodeName.toLowerCase() === "strong" ) { + window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); + } + }); + + var li = id(this.id); + li.className = bad ? "fail" : "pass"; + li.removeChild( li.firstChild ); + li.appendChild( b ); + li.appendChild( a ); + li.appendChild( ol ); + + } else { + for ( var i = 0; i < this.assertions.length; i++ ) { + if ( !this.assertions[i].result ) { + bad++; + config.stats.bad++; + config.moduleStats.bad++; + } + } + } + + try { + QUnit.reset(); + } catch(e) { + fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); + } + + QUnit.testDone( { + name: this.testName, + failed: bad, + passed: this.assertions.length - bad, + total: this.assertions.length + } ); + }, + + queue: function() { + var test = this; + synchronize(function() { + test.init(); + }); + function run() { + // each of these can by async + synchronize(function() { + test.setup(); + }); + synchronize(function() { + test.run(); + }); + synchronize(function() { + test.teardown(); + }); + synchronize(function() { + test.finish(); + }); + } + // defer when previous test run passed, if storage is available + var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName); + if (bad) { + run(); + } else { + synchronize(run); + }; + } + +}; + +var QUnit = { + + // call on start of module test to prepend name to all tests + module: function(name, testEnvironment) { + config.currentModule = name; + config.currentModuleTestEnviroment = testEnvironment; + }, + + asyncTest: function(testName, expected, callback) { + if ( arguments.length === 2 ) { + callback = expected; + expected = 0; + } + + QUnit.test(testName, expected, callback, true); + }, + + test: function(testName, expected, callback, async) { + var name = '' + testName + '', testEnvironmentArg; + + if ( arguments.length === 2 ) { + callback = expected; + expected = null; + } + // is 2nd argument a testEnvironment? + if ( expected && typeof expected === 'object') { + testEnvironmentArg = expected; + expected = null; + } + + if ( config.currentModule ) { + name = '' + config.currentModule + ": " + name; + } + + if ( !validTest(config.currentModule + ": " + testName) ) { + return; + } + + var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); + test.module = config.currentModule; + test.moduleTestEnvironment = config.currentModuleTestEnviroment; + test.queue(); + }, + + /** + * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. + */ + expect: function(asserts) { + config.current.expected = asserts; + }, + + /** + * Asserts true. + * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); + */ + ok: function(a, msg) { + a = !!a; + var details = { + result: a, + message: msg + }; + msg = escapeHtml(msg); + QUnit.log(details); + config.current.assertions.push({ + result: a, + message: msg + }); + }, + + /** + * Checks that the first two arguments are equal, with an optional message. + * Prints out both actual and expected values. + * + * Prefered to ok( actual == expected, message ) + * + * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); + * + * @param Object actual + * @param Object expected + * @param String message (optional) + */ + equal: function(actual, expected, message) { + QUnit.push(expected == actual, actual, expected, message); + }, + + notEqual: function(actual, expected, message) { + QUnit.push(expected != actual, actual, expected, message); + }, + + deepEqual: function(actual, expected, message) { + QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); + }, + + notDeepEqual: function(actual, expected, message) { + QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); + }, + + strictEqual: function(actual, expected, message) { + QUnit.push(expected === actual, actual, expected, message); + }, + + notStrictEqual: function(actual, expected, message) { + QUnit.push(expected !== actual, actual, expected, message); + }, + + raises: function(block, expected, message) { + var actual, ok = false; + + if (typeof expected === 'string') { + message = expected; + expected = null; + } + + try { + block(); + } catch (e) { + actual = e; + } + + if (actual) { + // we don't want to validate thrown error + if (!expected) { + ok = true; + // expected is a regexp + } else if (QUnit.objectType(expected) === "regexp") { + ok = expected.test(actual); + // expected is a constructor + } else if (actual instanceof expected) { + ok = true; + // expected is a validation function which returns true is validation passed + } else if (expected.call({}, actual) === true) { + ok = true; + } + } + + QUnit.ok(ok, message); + }, + + start: function() { + config.semaphore--; + if (config.semaphore > 0) { + // don't start until equal number of stop-calls + return; + } + if (config.semaphore < 0) { + // ignore if start is called more often then stop + config.semaphore = 0; + } + // A slight delay, to avoid any current callbacks + if ( defined.setTimeout ) { + window.setTimeout(function() { + if ( config.timeout ) { + clearTimeout(config.timeout); + } + + config.blocking = false; + process(); + }, 13); + } else { + config.blocking = false; + process(); + } + }, + + stop: function(timeout) { + config.semaphore++; + config.blocking = true; + + if ( timeout && defined.setTimeout ) { + clearTimeout(config.timeout); + config.timeout = window.setTimeout(function() { + QUnit.ok( false, "Test timed out" ); + QUnit.start(); + }, timeout); + } + } +}; + +// Backwards compatibility, deprecated +QUnit.equals = QUnit.equal; +QUnit.same = QUnit.deepEqual; + +// Maintain internal state +var config = { + // The queue of tests to run + queue: [], + + // block until document ready + blocking: true, + + // by default, run previously failed tests first + // very useful in combination with "Hide passed tests" checked + reorder: true, + + noglobals: false, + notrycatch: false +}; + +// Load paramaters +(function() { + var location = window.location || { search: "", protocol: "file:" }, + params = location.search.slice( 1 ).split( "&" ), + length = params.length, + urlParams = {}, + current; + + if ( params[ 0 ] ) { + for ( var i = 0; i < length; i++ ) { + current = params[ i ].split( "=" ); + current[ 0 ] = decodeURIComponent( current[ 0 ] ); + // allow just a key to turn on a flag, e.g., test.html?noglobals + current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; + urlParams[ current[ 0 ] ] = current[ 1 ]; + if ( current[ 0 ] in config ) { + config[ current[ 0 ] ] = current[ 1 ]; + } + } + } + + QUnit.urlParams = urlParams; + config.filter = urlParams.filter; + + // Figure out if we're running the tests from a server or not + QUnit.isLocal = !!(location.protocol === 'file:'); +})(); + +// Expose the API as global variables, unless an 'exports' +// object exists, in that case we assume we're in CommonJS +if ( typeof exports === "undefined" || typeof require === "undefined" ) { + extend(window, QUnit); + window.QUnit = QUnit; +} else { + extend(exports, QUnit); + exports.QUnit = QUnit; +} + +// define these after exposing globals to keep them in these QUnit namespace only +extend(QUnit, { + config: config, + + // Initialize the configuration options + init: function() { + extend(config, { + stats: { all: 0, bad: 0 }, + moduleStats: { all: 0, bad: 0 }, + started: +new Date, + updateRate: 1000, + blocking: false, + autostart: true, + autorun: false, + filter: "", + queue: [], + semaphore: 0 + }); + + var tests = id( "qunit-tests" ), + banner = id( "qunit-banner" ), + result = id( "qunit-testresult" ); + + if ( tests ) { + tests.innerHTML = ""; + } + + if ( banner ) { + banner.className = ""; + } + + if ( result ) { + result.parentNode.removeChild( result ); + } + + if ( tests ) { + result = document.createElement( "p" ); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore( result, tests ); + result.innerHTML = 'Running...
                                                                                                                                         '; + } + }, + + /** + * Resets the test setup. Useful for tests that modify the DOM. + * + * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. + */ + reset: function() { + if ( window.jQuery ) { + jQuery( "#qunit-fixture" ).html( config.fixture ); + } else { + var main = id( 'qunit-fixture' ); + if ( main ) { + main.innerHTML = config.fixture; + } + } + }, + + /** + * Trigger an event on an element. + * + * @example triggerEvent( document.body, "click" ); + * + * @param DOMElement elem + * @param String type + */ + triggerEvent: function( elem, type, event ) { + if ( document.createEvent ) { + event = document.createEvent("MouseEvents"); + event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + elem.dispatchEvent( event ); + + } else if ( elem.fireEvent ) { + elem.fireEvent("on"+type); + } + }, + + // Safe object type checking + is: function( type, obj ) { + return QUnit.objectType( obj ) == type; + }, + + objectType: function( obj ) { + if (typeof obj === "undefined") { + return "undefined"; + + // consider: typeof null === object + } + if (obj === null) { + return "null"; + } + + var type = Object.prototype.toString.call( obj ) + .match(/^\[object\s(.*)\]$/)[1] || ''; + + switch (type) { + case 'Number': + if (isNaN(obj)) { + return "nan"; + } else { + return "number"; + } + case 'String': + case 'Boolean': + case 'Array': + case 'Date': + case 'RegExp': + case 'Function': + return type.toLowerCase(); + } + if (typeof obj === "object") { + return "object"; + } + return undefined; + }, + + push: function(result, actual, expected, message) { + var details = { + result: result, + message: message, + actual: actual, + expected: expected + }; + + message = escapeHtml(message) || (result ? "okay" : "failed"); + message = '' + message + ""; + expected = escapeHtml(QUnit.jsDump.parse(expected)); + actual = escapeHtml(QUnit.jsDump.parse(actual)); + var output = message + ''; + if (actual != expected) { + output += ''; + output += ''; + } + if (!result) { + var source = sourceFromStacktrace(); + if (source) { + details.source = source; + output += ''; + } + } + output += "
                                                                                                                                        Expected:
                                                                                                                                        ' + expected + '
                                                                                                                                        Result:
                                                                                                                                        ' + actual + '
                                                                                                                                        Diff:
                                                                                                                                        ' + QUnit.diff(expected, actual) +'
                                                                                                                                        Source:
                                                                                                                                        ' + escapeHtml(source) + '
                                                                                                                                        "; + + QUnit.log(details); + + config.current.assertions.push({ + result: !!result, + message: output + }); + }, + + url: function( params ) { + params = extend( extend( {}, QUnit.urlParams ), params ); + var querystring = "?", + key; + for ( key in params ) { + querystring += encodeURIComponent( key ) + "=" + + encodeURIComponent( params[ key ] ) + "&"; + } + return window.location.pathname + querystring.slice( 0, -1 ); + }, + + // Logging callbacks; all receive a single argument with the listed properties + // run test/logs.html for any related changes + begin: function() {}, + // done: { failed, passed, total, runtime } + done: function() {}, + // log: { result, actual, expected, message } + log: function() {}, + // testStart: { name } + testStart: function() {}, + // testDone: { name, failed, passed, total } + testDone: function() {}, + // moduleStart: { name } + moduleStart: function() {}, + // moduleDone: { name, failed, passed, total } + moduleDone: function() {} +}); + +if ( typeof document === "undefined" || document.readyState === "complete" ) { + config.autorun = true; +} + +addEvent(window, "load", function() { + QUnit.begin({}); + + // Initialize the config, saving the execution queue + var oldconfig = extend({}, config); + QUnit.init(); + extend(config, oldconfig); + + config.blocking = false; + + var userAgent = id("qunit-userAgent"); + if ( userAgent ) { + userAgent.innerHTML = navigator.userAgent; + } + var banner = id("qunit-header"); + if ( banner ) { + banner.innerHTML = ' ' + banner.innerHTML + ' ' + + '' + + ''; + addEvent( banner, "change", function( event ) { + var params = {}; + params[ event.target.name ] = event.target.checked ? true : undefined; + window.location = QUnit.url( params ); + }); + } + + var toolbar = id("qunit-testrunner-toolbar"); + if ( toolbar ) { + var filter = document.createElement("input"); + filter.type = "checkbox"; + filter.id = "qunit-filter-pass"; + addEvent( filter, "click", function() { + var ol = document.getElementById("qunit-tests"); + if ( filter.checked ) { + ol.className = ol.className + " hidepass"; + } else { + var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; + ol.className = tmp.replace(/ hidepass /, " "); + } + if ( defined.sessionStorage ) { + if (filter.checked) { + sessionStorage.setItem("qunit-filter-passed-tests", "true"); + } else { + sessionStorage.removeItem("qunit-filter-passed-tests"); + } + } + }); + if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { + filter.checked = true; + var ol = document.getElementById("qunit-tests"); + ol.className = ol.className + " hidepass"; + } + toolbar.appendChild( filter ); + + var label = document.createElement("label"); + label.setAttribute("for", "qunit-filter-pass"); + label.innerHTML = "Hide passed tests"; + toolbar.appendChild( label ); + } + + var main = id('qunit-fixture'); + if ( main ) { + config.fixture = main.innerHTML; + } + + if (config.autostart) { + QUnit.start(); + } +}); + +function done() { + config.autorun = true; + + // Log the last module results + if ( config.currentModule ) { + QUnit.moduleDone( { + name: config.currentModule, + failed: config.moduleStats.bad, + passed: config.moduleStats.all - config.moduleStats.bad, + total: config.moduleStats.all + } ); + } + + var banner = id("qunit-banner"), + tests = id("qunit-tests"), + runtime = +new Date - config.started, + passed = config.stats.all - config.stats.bad, + html = [ + 'Tests completed in ', + runtime, + ' milliseconds.
                                                                                                                                        ', + '', + passed, + ' tests of ', + config.stats.all, + ' passed, ', + config.stats.bad, + ' failed.' + ].join(''); + + if ( banner ) { + banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); + } + + if ( tests ) { + id( "qunit-testresult" ).innerHTML = html; + } + + if ( typeof document !== "undefined" && document.title ) { + // show ✖ for good, ✔ for bad suite result in title + // use escape sequences in case file gets loaded with non-utf-8-charset + document.title = (config.stats.bad ? "\u2716" : "\u2714") + " " + document.title; + } + + QUnit.done( { + failed: config.stats.bad, + passed: passed, + total: config.stats.all, + runtime: runtime + } ); +} + +function validTest( name ) { + var filter = config.filter, + run = false; + + if ( !filter ) { + return true; + } + + var not = filter.charAt( 0 ) === "!"; + if ( not ) { + filter = filter.slice( 1 ); + } + + if ( name.indexOf( filter ) !== -1 ) { + return !not; + } + + if ( not ) { + run = true; + } + + return run; +} + +// so far supports only Firefox, Chrome and Opera (buggy) +// could be extended in the future to use something like https://github.com/csnover/TraceKit +function sourceFromStacktrace() { + try { + throw new Error(); + } catch ( e ) { + if (e.stacktrace) { + // Opera + return e.stacktrace.split("\n")[6]; + } else if (e.stack) { + // Firefox, Chrome + return e.stack.split("\n")[4]; + } + } +} + +function escapeHtml(s) { + if (!s) { + return ""; + } + s = s + ""; + return s.replace(/[\&"<>\\]/g, function(s) { + switch(s) { + case "&": return "&"; + case "\\": return "\\\\"; + case '"': return '\"'; + case "<": return "<"; + case ">": return ">"; + default: return s; + } + }); +} + +function synchronize( callback ) { + config.queue.push( callback ); + + if ( config.autorun && !config.blocking ) { + process(); + } +} + +function process() { + var start = (new Date()).getTime(); + + while ( config.queue.length && !config.blocking ) { + if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { + config.queue.shift()(); + } else { + window.setTimeout( process, 13 ); + break; + } + } + if (!config.blocking && !config.queue.length) { + done(); + } +} + +function saveGlobal() { + config.pollution = []; + + if ( config.noglobals ) { + for ( var key in window ) { + config.pollution.push( key ); + } + } +} + +function checkPollution( name ) { + var old = config.pollution; + saveGlobal(); + + var newGlobals = diff( config.pollution, old ); + if ( newGlobals.length > 0 ) { + ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); + } + + var deletedGlobals = diff( old, config.pollution ); + if ( deletedGlobals.length > 0 ) { + ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); + } +} + +// returns a new Array with the elements that are in a but not in b +function diff( a, b ) { + var result = a.slice(); + for ( var i = 0; i < result.length; i++ ) { + for ( var j = 0; j < b.length; j++ ) { + if ( result[i] === b[j] ) { + result.splice(i, 1); + i--; + break; + } + } + } + return result; +} + +function fail(message, exception, callback) { + if ( typeof console !== "undefined" && console.error && console.warn ) { + console.error(message); + console.error(exception); + console.warn(callback.toString()); + + } else if ( window.opera && opera.postError ) { + opera.postError(message, exception, callback.toString); + } +} + +function extend(a, b) { + for ( var prop in b ) { + if ( b[prop] === undefined ) { + delete a[prop]; + } else { + a[prop] = b[prop]; + } + } + + return a; +} + +function addEvent(elem, type, fn) { + if ( elem.addEventListener ) { + elem.addEventListener( type, fn, false ); + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, fn ); + } else { + fn(); + } +} + +function id(name) { + return !!(typeof document !== "undefined" && document && document.getElementById) && + document.getElementById( name ); +} + +// Test for equality any JavaScript type. +// Discussions and reference: http://philrathe.com/articles/equiv +// Test suites: http://philrathe.com/tests/equiv +// Author: Philippe Rathé +QUnit.equiv = function () { + + var innerEquiv; // the real equiv function + var callers = []; // stack to decide between skip/abort functions + var parents = []; // stack to avoiding loops from circular referencing + + // Call the o related callback with the given arguments. + function bindCallbacks(o, callbacks, args) { + var prop = QUnit.objectType(o); + if (prop) { + if (QUnit.objectType(callbacks[prop]) === "function") { + return callbacks[prop].apply(callbacks, args); + } else { + return callbacks[prop]; // or undefined + } + } + } + + var callbacks = function () { + + // for string, boolean, number and null + function useStrictEquality(b, a) { + if (b instanceof a.constructor || a instanceof b.constructor) { + // to catch short annotaion VS 'new' annotation of a declaration + // e.g. var i = 1; + // var j = new Number(1); + return a == b; + } else { + return a === b; + } + } + + return { + "string": useStrictEquality, + "boolean": useStrictEquality, + "number": useStrictEquality, + "null": useStrictEquality, + "undefined": useStrictEquality, + + "nan": function (b) { + return isNaN(b); + }, + + "date": function (b, a) { + return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); + }, + + "regexp": function (b, a) { + return QUnit.objectType(b) === "regexp" && + a.source === b.source && // the regex itself + a.global === b.global && // and its modifers (gmi) ... + a.ignoreCase === b.ignoreCase && + a.multiline === b.multiline; + }, + + // - skip when the property is a method of an instance (OOP) + // - abort otherwise, + // initial === would have catch identical references anyway + "function": function () { + var caller = callers[callers.length - 1]; + return caller !== Object && + typeof caller !== "undefined"; + }, + + "array": function (b, a) { + var i, j, loop; + var len; + + // b could be an object literal here + if ( ! (QUnit.objectType(b) === "array")) { + return false; + } + + len = a.length; + if (len !== b.length) { // safe and faster + return false; + } + + //track reference to avoid circular references + parents.push(a); + for (i = 0; i < len; i++) { + loop = false; + for(j=0;j= 0) { + type = "array"; + } else { + type = typeof obj; + } + return type; + }, + separator:function() { + return this.multiline ? this.HTML ? '
                                                                                                                                        ' : '\n' : this.HTML ? ' ' : ' '; + }, + indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing + if ( !this.multiline ) + return ''; + var chr = this.indentChar; + if ( this.HTML ) + chr = chr.replace(/\t/g,' ').replace(/ /g,' '); + return Array( this._depth_ + (extra||0) ).join(chr); + }, + up:function( a ) { + this._depth_ += a || 1; + }, + down:function( a ) { + this._depth_ -= a || 1; + }, + setParser:function( name, parser ) { + this.parsers[name] = parser; + }, + // The next 3 are exposed so you can use them + quote:quote, + literal:literal, + join:join, + // + _depth_: 1, + // This is the list of parsers, to modify them, use jsDump.setParser + parsers:{ + window: '[Window]', + document: '[Document]', + error:'[ERROR]', //when no parser is found, shouldn't happen + unknown: '[Unknown]', + 'null':'null', + 'undefined':'undefined', + 'function':function( fn ) { + var ret = 'function', + name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE + if ( name ) + ret += ' ' + name; + ret += '('; + + ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); + return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); + }, + array: array, + nodelist: array, + arguments: array, + object:function( map ) { + var ret = [ ]; + QUnit.jsDump.up(); + for ( var key in map ) + ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) ); + QUnit.jsDump.down(); + return join( '{', ret, '}' ); + }, + node:function( node ) { + var open = QUnit.jsDump.HTML ? '<' : '<', + close = QUnit.jsDump.HTML ? '>' : '>'; + + var tag = node.nodeName.toLowerCase(), + ret = open + tag; + + for ( var a in QUnit.jsDump.DOMAttrs ) { + var val = node[QUnit.jsDump.DOMAttrs[a]]; + if ( val ) + ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' ); + } + return ret + close + open + '/' + tag + close; + }, + functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function + var l = fn.length; + if ( !l ) return ''; + + var args = Array(l); + while ( l-- ) + args[l] = String.fromCharCode(97+l);//97 is 'a' + return ' ' + args.join(', ') + ' '; + }, + key:quote, //object calls it internally, the key part of an item in a map + functionCode:'[code]', //function calls it internally, it's the content of the function + attribute:quote, //node calls it internally, it's an html attribute value + string:quote, + date:quote, + regexp:literal, //regex + number:literal, + 'boolean':literal + }, + DOMAttrs:{//attributes to dump from nodes, name=>realName + id:'id', + name:'name', + 'class':'className' + }, + HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) + indentChar:' ',//indentation unit + multiline:true //if true, items in a collection, are separated by a \n, else just a space. + }; + + return jsDump; +})(); + +// from Sizzle.js +function getText( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += getText( elem.childNodes ); + } + } + + return ret; +}; + +/* + * Javascript Diff Algorithm + * By John Resig (http://ejohn.org/) + * Modified by Chu Alan "sprite" + * + * Released under the MIT license. + * + * More Info: + * http://ejohn.org/projects/javascript-diff-algorithm/ + * + * Usage: QUnit.diff(expected, actual) + * + * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over" + */ +QUnit.diff = (function() { + function diff(o, n){ + var ns = new Object(); + var os = new Object(); + + for (var i = 0; i < n.length; i++) { + if (ns[n[i]] == null) + ns[n[i]] = { + rows: new Array(), + o: null + }; + ns[n[i]].rows.push(i); + } + + for (var i = 0; i < o.length; i++) { + if (os[o[i]] == null) + os[o[i]] = { + rows: new Array(), + n: null + }; + os[o[i]].rows.push(i); + } + + for (var i in ns) { + if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { + n[ns[i].rows[0]] = { + text: n[ns[i].rows[0]], + row: os[i].rows[0] + }; + o[os[i].rows[0]] = { + text: o[os[i].rows[0]], + row: ns[i].rows[0] + }; + } + } + + for (var i = 0; i < n.length - 1; i++) { + if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && + n[i + 1] == o[n[i].row + 1]) { + n[i + 1] = { + text: n[i + 1], + row: n[i].row + 1 + }; + o[n[i].row + 1] = { + text: o[n[i].row + 1], + row: i + 1 + }; + } + } + + for (var i = n.length - 1; i > 0; i--) { + if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && + n[i - 1] == o[n[i].row - 1]) { + n[i - 1] = { + text: n[i - 1], + row: n[i].row - 1 + }; + o[n[i].row - 1] = { + text: o[n[i].row - 1], + row: i - 1 + }; + } + } + + return { + o: o, + n: n + }; + } + + return function(o, n){ + o = o.replace(/\s+$/, ''); + n = n.replace(/\s+$/, ''); + var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); + + var str = ""; + + var oSpace = o.match(/\s+/g); + if (oSpace == null) { + oSpace = [" "]; + } + else { + oSpace.push(" "); + } + var nSpace = n.match(/\s+/g); + if (nSpace == null) { + nSpace = [" "]; + } + else { + nSpace.push(" "); + } + + if (out.n.length == 0) { + for (var i = 0; i < out.o.length; i++) { + str += '' + out.o[i] + oSpace[i] + ""; + } + } + else { + if (out.n[0].text == null) { + for (n = 0; n < out.o.length && out.o[n].text == null; n++) { + str += '' + out.o[n] + oSpace[n] + ""; + } + } + + for (var i = 0; i < out.n.length; i++) { + if (out.n[i].text == null) { + str += '' + out.n[i] + nSpace[i] + ""; + } + else { + var pre = ""; + + for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { + pre += '' + out.o[n] + oSpace[n] + ""; + } + str += " " + out.n[i].text + nSpace[i] + pre; + } + } + } + + return str; + }; +})(); + +})(this); diff --git a/test/saved-data-0000.json b/test/saved-data-0000.json new file mode 100644 index 00000000..aec231ce --- /dev/null +++ b/test/saved-data-0000.json @@ -0,0 +1,46 @@ +{ + "targets": [ + { + "id": "Target0", + "name": "Target1", + "element": "test-target-1" + } + ], + "media": [ + { + "id": "Media0", + "name": "Media0", + "url": [ + "../../external/popcorn-js/test/trailer.ogv", + "../../external/popcorn-js/test/trailer.mp4" + ], + "target": "mediaDiv", + "duration": 64.54166412353516, + "tracks": [ + { + "name": "Track0", + "id": "Track0", + "trackEvents": [] + }, + { + "name": "Track1", + "id": "Track1", + "trackEvents": [ + { + "id": "TrackEvent0", + "type": "text", + "popcornOptions": { + "start": 0, + "end": 3, + "text": "test", + "target": "test-target-1" + }, + "track": "Track0", + "name": "TrackEvent0" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/selenium/testcases_tracks.py b/test/selenium/testcases_tracks.py new file mode 100644 index 00000000..0d6671e9 --- /dev/null +++ b/test/selenium/testcases_tracks.py @@ -0,0 +1,78 @@ +from selenium import selenium +import unittest, time, re, sys + +sys.path.append( "../../../../content/" ); +import testvars + +global btv +btv = testvars.ButterTestVariables + +class testcase_TrackDrop(unittest.TestCase): + + def setUp( self ): + self.verificationErrors = [] + self.selenium = selenium( btv["Host"], + btv["Port"], + btv["Browser"], + btv["Grid"] + ) + self.selenium.start() + + def test_case(self): + sel = self.selenium + sel.open(btv["ButterTestPage"]) + sel.set_speed( 500 ) + sel.wait_for_page_to_load("7500") + for i in range(60): + try: + if sel.is_element_present("id=TrackEventView1"): break + except: pass + time.sleep(1) + else: self.fail("time out") + sel.mouse_move_at("id=TrackEventView1", "5,0") + sel.mouse_down_at("id=TrackEventView1", "5,0") + self.failUnless(sel.is_element_present("id=TrackView2")) + sel.mouse_move_at("id=TrackView2", "300,5") + sel.mouse_up_at("id=TrackView2", "300,5") + self.failUnless(sel.is_element_present("id=TrackEventView1")) + + def tearDown(self): + self.selenium.stop() + self.assertEqual([], self.verificationErrors) + +class testcase_DeleteTrack(unittest.TestCase): + def setUp(self): + self.verificationErrors = [] + self.selenium = selenium( btv["Host"], + btv["Port"], + btv["Browser"], + btv["Grid"] + ) + self.selenium.start() + + def test_delete_track(self): + sel = self.selenium + sel.open(btv["ButterTestPage"]) + sel.wait_for_page_to_load("7500") + for i in range(60): + try: + if sel.is_element_present("id=TrackView2"): break + except: pass + time.sleep(1) + else: self.fail("time out") + self.failUnless(sel.is_element_present("id=TrackView2")) + self.failUnless(sel.is_element_present("id=TrackView1")) + self.failUnless(sel.is_element_present("id=track-handle-Track0")) + self.failUnless(sel.is_element_present("id=TrackEventView1")) + # sel.asser("id=track-handle-Track2", "0,5") + sel.mouse_down_at("id=track-handle-Track2", "0,5") + sel.mouse_move_at("id=track-handle-Track1", "5,15") + sel.mouse_up_at("id=track-handle-Track1", "5,15") + self.failUnless(sel.is_element_present("id=TrackView2")) + self.failUnless(sel.is_element_present("id=TrackView1")) + self.failUnless(sel.is_element_present("id=track-handle-Track0")) + self.failUnless(sel.is_element_present("id=TrackEventView1")) + + def tearDown(self): + self.selenium.stop() + self.assertEqual([], self.verificationErrors) diff --git a/test/test-config-module.json b/test/test-config-module.json new file mode 100644 index 00000000..a72d70cd --- /dev/null +++ b/test/test-config-module.json @@ -0,0 +1,23 @@ +{ + "name": "test-config-core", + "savedDataUrl": "../saved-data-0000.json", + "scrapePage": false, + "baseDir": "../../", + "makeVideoURLsUnique": false, + "recover": "purge", + "ui": { + "enabled": false + }, + "plugin": { + "plugins": [ + { + "type": "text", + "path": "../../external/popcorn-js/plugins/text/popcorn.text.js" + }, + { + "type": "image", + "path": "../../external/popcorn-js/plugins/image/popcorn.image.js" + } + ] + } +} diff --git a/test/test-utils.js b/test/test-utils.js new file mode 100644 index 00000000..4d4b20c1 --- /dev/null +++ b/test/test-utils.js @@ -0,0 +1,24 @@ +window._testInitCallback = function(){}; +window._testBeforeCallback = function(){}; +window._testAfterCallback = function(){}; + +function createButterCore( callback ){ + + Butter.init({ + config: "../test-config-core.json", + debug: false, + ready: function( butter ){ + callback( butter ); + } + }); +} + +function createButterModule( callback ){ + Butter.init({ + config: "../test-config-module.json", + debug: false, + ready: function( butter ){ + callback( butter ); + } + }); +} \ No newline at end of file diff --git a/test/timecode/index.html b/test/timecode/index.html new file mode 100644 index 00000000..d603506b --- /dev/null +++ b/test/timecode/index.html @@ -0,0 +1,125 @@ + + + + Butter Test Suite [timecode] + + + + + + + + +

                                                                                                                                        Butter API Test Suite [timecode]

                                                                                                                                        +

                                                                                                                                        +
                                                                                                                                        +

                                                                                                                                        +
                                                                                                                                          + + diff --git a/test/track/index.html b/test/track/index.html new file mode 100644 index 00000000..153e9949 --- /dev/null +++ b/test/track/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                                                                                                                                          + Butter Test Suite [ Page Module ] + +

                                                                                                                                          +

                                                                                                                                          +

                                                                                                                                          +
                                                                                                                                            + + diff --git a/test/track/track-addTrackEvent.html b/test/track/track-addTrackEvent.html new file mode 100644 index 00000000..a6ee1d21 --- /dev/null +++ b/test/track/track-addTrackEvent.html @@ -0,0 +1,53 @@ + + + + Butter Test Suite [Track] + + + + + + + + + +

                                                                                                                                            Butter API Test Suite [Track]

                                                                                                                                            +

                                                                                                                                            +
                                                                                                                                            +

                                                                                                                                            +
                                                                                                                                              +
                                                                                                                                              +
                                                                                                                                              +
                                                                                                                                              + + diff --git a/test/track/track-deselectEvents.html b/test/track/track-deselectEvents.html new file mode 100644 index 00000000..fbd0697c --- /dev/null +++ b/test/track/track-deselectEvents.html @@ -0,0 +1,55 @@ + + + + Butter Test Suite [Track] + + + + + + + + + +

                                                                                                                                              Butter API Test Suite [Track]

                                                                                                                                              +

                                                                                                                                              +
                                                                                                                                              +

                                                                                                                                              +
                                                                                                                                                +
                                                                                                                                                +
                                                                                                                                                +
                                                                                                                                                + + diff --git a/test/track/track-getTrackEventById.html b/test/track/track-getTrackEventById.html new file mode 100644 index 00000000..dcffc932 --- /dev/null +++ b/test/track/track-getTrackEventById.html @@ -0,0 +1,45 @@ + + + + Butter Test Suite [Track] + + + + + + + + + +

                                                                                                                                                Butter API Test Suite [Track]

                                                                                                                                                +

                                                                                                                                                +
                                                                                                                                                +

                                                                                                                                                +
                                                                                                                                                  +
                                                                                                                                                  +
                                                                                                                                                  +
                                                                                                                                                  + + diff --git a/test/track/track-getTrackEventByName.html b/test/track/track-getTrackEventByName.html new file mode 100644 index 00000000..993aad93 --- /dev/null +++ b/test/track/track-getTrackEventByName.html @@ -0,0 +1,36 @@ + + + + Butter Test Suite [Track] + + + + + + + + + +

                                                                                                                                                  Butter API Test Suite [Track]

                                                                                                                                                  +

                                                                                                                                                  +
                                                                                                                                                  +

                                                                                                                                                  +
                                                                                                                                                    +
                                                                                                                                                    +
                                                                                                                                                    +
                                                                                                                                                    + + diff --git a/test/track/track-removeEmptyTrack.html b/test/track/track-removeEmptyTrack.html new file mode 100644 index 00000000..7f01d0e6 --- /dev/null +++ b/test/track/track-removeEmptyTrack.html @@ -0,0 +1,50 @@ + + + + Butter Test Suite [Track] + + + + + + + + + +

                                                                                                                                                    Butter API Test Suite [Track]

                                                                                                                                                    +

                                                                                                                                                    +
                                                                                                                                                    +

                                                                                                                                                    +
                                                                                                                                                      +
                                                                                                                                                      +
                                                                                                                                                      +
                                                                                                                                                      + + diff --git a/test/track/track-removeTrackEvent.html b/test/track/track-removeTrackEvent.html new file mode 100644 index 00000000..bf3247c9 --- /dev/null +++ b/test/track/track-removeTrackEvent.html @@ -0,0 +1,48 @@ + + + + Butter Test Suite [Track] + + + + + + + + + +

                                                                                                                                                      Butter API Test Suite [Track]

                                                                                                                                                      +

                                                                                                                                                      +
                                                                                                                                                      +

                                                                                                                                                      +
                                                                                                                                                        +
                                                                                                                                                        +
                                                                                                                                                        +
                                                                                                                                                        + + diff --git a/test/track/track-tests.conf b/test/track/track-tests.conf new file mode 100644 index 00000000..df9fc7ab --- /dev/null +++ b/test/track/track-tests.conf @@ -0,0 +1,10 @@ +{ + "track": { + "addTrackEvent": "test/track/track-addTrackEvent.html", + "deselectEvents": "test/track/track-deselectEvents.html", + "getTrackEventById": "test/track/track-getTrackEventById.html", + "getTrackEventByName": "test/track/track-getTrackEventByName.html", + "removeTrackEvent": "test/track/track-removeTrackEvent.html", + "removeEmptyTrack": "test/track/track-removeEmptyTrack.html" + } +} diff --git a/test/trackevent/index.html b/test/trackevent/index.html new file mode 100644 index 00000000..fd43bd05 --- /dev/null +++ b/test/trackevent/index.html @@ -0,0 +1,26 @@ + + + + Butter Test Suite Runner + + + + + + +

                                                                                                                                                        + Butter Test Suite [ Page Module ] + +

                                                                                                                                                        +

                                                                                                                                                        +

                                                                                                                                                        +
                                                                                                                                                          + + diff --git a/test/trackevent/trackevent-preview-text.html b/test/trackevent/trackevent-preview-text.html new file mode 100644 index 00000000..f4018c9d --- /dev/null +++ b/test/trackevent/trackevent-preview-text.html @@ -0,0 +1,55 @@ + + + + Butter Test Suite [Track Event] + + + + + + + + + +

                                                                                                                                                          Butter API Test Suite [Track Event]

                                                                                                                                                          +

                                                                                                                                                          +
                                                                                                                                                          +

                                                                                                                                                          +
                                                                                                                                                            +
                                                                                                                                                            +
                                                                                                                                                            +
                                                                                                                                                            +
                                                                                                                                                            + + diff --git a/test/trackevent/trackevent-tests.conf b/test/trackevent/trackevent-tests.conf new file mode 100644 index 00000000..5ce1b45f --- /dev/null +++ b/test/trackevent/trackevent-tests.conf @@ -0,0 +1,7 @@ +{ + "trackevent": { + "updateValid": "test/trackevent/trackevent-updateValid.html", + "updateInvalid": "test/trackevent/trackevent-updateInvalid.html", + "previewText": "test/trackevent/trackevent-preview-text.html" + } +} diff --git a/test/trackevent/trackevent-updateInvalid.html b/test/trackevent/trackevent-updateInvalid.html new file mode 100644 index 00000000..34adc70e --- /dev/null +++ b/test/trackevent/trackevent-updateInvalid.html @@ -0,0 +1,54 @@ + + + + Butter Test Suite [Track Event] + + + + + + + + + +

                                                                                                                                                            Butter API Test Suite [Track Event]

                                                                                                                                                            +

                                                                                                                                                            +
                                                                                                                                                            +

                                                                                                                                                            +
                                                                                                                                                              +
                                                                                                                                                              +
                                                                                                                                                              +
                                                                                                                                                              +
                                                                                                                                                              + + diff --git a/test/trackevent/trackevent-updateValid.html b/test/trackevent/trackevent-updateValid.html new file mode 100644 index 00000000..58595634 --- /dev/null +++ b/test/trackevent/trackevent-updateValid.html @@ -0,0 +1,62 @@ + + + + Butter Test Suite [Track Event] + + + + + + + + + +

                                                                                                                                                              Butter API Test Suite [Track Event]

                                                                                                                                                              +

                                                                                                                                                              +
                                                                                                                                                              +

                                                                                                                                                              +
                                                                                                                                                                +
                                                                                                                                                                +
                                                                                                                                                                +
                                                                                                                                                                +
                                                                                                                                                                + + diff --git a/test/ui/index.html b/test/ui/index.html new file mode 100644 index 00000000..b6492bd1 --- /dev/null +++ b/test/ui/index.html @@ -0,0 +1,72 @@ + + + + Butter UI Tests + + + + + + + + + + + + + + + + + + +
                                                                                                                                                                +
                                                                                                                                                                +

                                                                                                                                                                Area 1

                                                                                                                                                                +
                                                                                                                                                                +
                                                                                                                                                                +
                                                                                                                                                                +

                                                                                                                                                                Area 2

                                                                                                                                                                +
                                                                                                                                                                +
                                                                                                                                                                + +
                                                                                                                                                                +

                                                                                                                                                                Butter UI Test Template

                                                                                                                                                                +

                                                                                                                                                                +
                                                                                                                                                                +

                                                                                                                                                                +
                                                                                                                                                                  +
                                                                                                                                                                  + + diff --git a/test/ui/js/EventUtils.js b/test/ui/js/EventUtils.js new file mode 100644 index 00000000..6cb8a490 --- /dev/null +++ b/test/ui/js/EventUtils.js @@ -0,0 +1,722 @@ +/** + * EventUtils.js from mozilla-central + * + * Source: http://mxr.mozilla.org/mozilla-central/source/testing/mochitest/tests/SimpleTest/EventUtils.js + * License: http://www.mozilla.org/foundation/licensing.html + * Depends on SpecialPowers: https://github.com/humphd/SpecialPowers + * + * EventUtils provides some utility methods for creating and sending DOM events. + * Current methods: + * sendMouseEvent + * sendChar + * sendString + * sendKey + * synthesizeMouse + * synthesizeMouseAtCenter + * synthesizeMouseScroll + * synthesizeKey + * synthesizeMouseExpectEvent + * synthesizeKeyExpectEvent + * + * When adding methods to this file, please add a performance test for it. + */ + +// Make sure window.SpecialPowers exists +(function( global ) { + if ( !"SpecialPowers" in global ) { + throw "Error: Missing SpecialPowers extension, see https://github.com/humphd/SpecialPowers"; + } +})(window); + +/** + * Send a mouse event to the node aTarget (aTarget can be an id, or an + * actual node) . The "event" passed in to aEvent is just a JavaScript + * object with the properties set that the real mouse event object should + * have. This includes the type of the mouse event. + * E.g. to send an click event to the node with id 'node' you might do this: + * + * sendMouseEvent({type:'click'}, 'node'); + */ +function getElement(id) { + return ((typeof(id) == "string") ? + document.getElementById(id) : id); +}; + +this.$ = this.getElement; + +function sendMouseEvent(aEvent, aTarget, aWindow) { + if (['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) { + throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'"); + } + + if (!aWindow) { + aWindow = window; + } + + if (!(aTarget instanceof Element)) { + aTarget = aWindow.document.getElementById(aTarget); + } + + var event = aWindow.document.createEvent('MouseEvent'); + + var typeArg = aEvent.type; + var canBubbleArg = true; + var cancelableArg = true; + var viewArg = aWindow; + var detailArg = aEvent.detail || (aEvent.type == 'click' || + aEvent.type == 'mousedown' || + aEvent.type == 'mouseup' ? 1 : + aEvent.type == 'dblclick'? 2 : 0); + var screenXArg = aEvent.screenX || 0; + var screenYArg = aEvent.screenY || 0; + var clientXArg = aEvent.clientX || 0; + var clientYArg = aEvent.clientY || 0; + var ctrlKeyArg = aEvent.ctrlKey || false; + var altKeyArg = aEvent.altKey || false; + var shiftKeyArg = aEvent.shiftKey || false; + var metaKeyArg = aEvent.metaKey || false; + var buttonArg = aEvent.button || 0; + var relatedTargetArg = aEvent.relatedTarget || null; + + event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg, + screenXArg, screenYArg, clientXArg, clientYArg, + ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg, + buttonArg, relatedTargetArg); + + SpecialPowers.dispatchEvent(aWindow, aTarget, event); +} + +/** + * Send the char aChar to the focused element. This method handles casing of + * chars (sends the right charcode, and sends a shift key for uppercase chars). + * No other modifiers are handled at this point. + * + * For now this method only works for English letters (lower and upper case) + * and the digits 0-9. + */ +function sendChar(aChar, aWindow) { + // DOM event charcodes match ASCII (JS charcodes) for a-zA-Z0-9. + var hasShift = (aChar == aChar.toUpperCase()); + synthesizeKey(aChar, { shiftKey: hasShift }, aWindow); +} + +/** + * Send the string aStr to the focused element. + * + * For now this method only works for English letters (lower and upper case) + * and the digits 0-9. + */ +function sendString(aStr, aWindow) { + for (var i = 0; i < aStr.length; ++i) { + sendChar(aStr.charAt(i), aWindow); + } +} + +/** + * Send the non-character key aKey to the focused node. + * The name of the key should be the part that comes after "DOM_VK_" in the + * KeyEvent constant name for this key. + * No modifiers are handled at this point. + */ +function sendKey(aKey, aWindow) { + var keyName = "VK_" + aKey.toUpperCase(); + synthesizeKey(keyName, { shiftKey: false }, aWindow); +} + +/** + * Parse the key modifier flags from aEvent. Used to share code between + * synthesizeMouse and synthesizeKey. + */ +function _parseModifiers(aEvent) +{ + const nsIDOMWindowUtils = Components.interfaces.nsIDOMWindowUtils; + var mval = 0; + if (aEvent.shiftKey) { + mval |= nsIDOMWindowUtils.MODIFIER_SHIFT; + } + if (aEvent.ctrlKey) { + mval |= nsIDOMWindowUtils.MODIFIER_CONTROL; + } + if (aEvent.altKey) { + mval |= nsIDOMWindowUtils.MODIFIER_ALT; + } + if (aEvent.metaKey) { + mval |= nsIDOMWindowUtils.MODIFIER_META; + } + if (aEvent.accelKey) { + mval |= (navigator.platform.indexOf("Mac") >= 0) ? + nsIDOMWindowUtils.MODIFIER_META : nsIDOMWindowUtils.MODIFIER_CONTROL; + } + if (aEvent.altGrKey) { + mval |= nsIDOMWindowUtils.MODIFIER_ALTGRAPH; + } + if (aEvent.capsLockKey) { + mval |= nsIDOMWindowUtils.MODIFIER_CAPSLOCK; + } + if (aEvent.fnKey) { + mval |= nsIDOMWindowUtils.MODIFIER_FN; + } + if (aEvent.numLockKey) { + mval |= nsIDOMWindowUtils.MODIFIER_NUMLOCK; + } + if (aEvent.scrollLockKey) { + mval |= nsIDOMWindowUtils.MODIFIER_SCROLL; + } + if (aEvent.symbolLockKey) { + mval |= nsIDOMWindowUtils.MODIFIER_SYMBOLLOCK; + } + if (aEvent.winKey) { + mval |= nsIDOMWindowUtils.MODIFIER_WIN; + } + + return mval; +} + +/** + * Synthesize a mouse event on a target. The actual client point is determined + * by taking the aTarget's client box and offseting it by aOffsetX and + * aOffsetY. This allows mouse clicks to be simulated by calling this method. + * + * aEvent is an object which may contain the properties: + * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type + * + * If the type is specified, an mouse event of that type is fired. Otherwise, + * a mousedown followed by a mouse up is performed. + * + * aWindow is optional, and defaults to the current window object. + */ +function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) +{ + var rect = aTarget.getBoundingClientRect(); + synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY, + aEvent, aWindow); +} + +/* + * Synthesize a mouse event at a particular point in aWindow. + * + * aEvent is an object which may contain the properties: + * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type + * + * If the type is specified, an mouse event of that type is fired. Otherwise, + * a mousedown followed by a mouse up is performed. + * + * aWindow is optional, and defaults to the current window object. + */ +function synthesizeMouseAtPoint(left, top, aEvent, aWindow) +{ + var utils = _getDOMWindowUtils(aWindow); + + if (utils) { + var button = aEvent.button || 0; + var clickCount = aEvent.clickCount || 1; + var modifiers = _parseModifiers(aEvent); + + if (("type" in aEvent) && aEvent.type) { + utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers); + } + else { + utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers); + utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers); + } + } +} + +// Call synthesizeMouse with coordinates at the center of aTarget. +function synthesizeMouseAtCenter(aTarget, aEvent, aWindow) +{ + var rect = aTarget.getBoundingClientRect(); + synthesizeMouse(aTarget, rect.width / 2, rect.height / 2, aEvent, + aWindow); +} + +/** + * Synthesize a mouse scroll event on a target. The actual client point is determined + * by taking the aTarget's client box and offseting it by aOffsetX and + * aOffsetY. + * + * aEvent is an object which may contain the properties: + * shiftKey, ctrlKey, altKey, metaKey, accessKey, button, type, axis, delta, hasPixels + * + * If the type is specified, a mouse scroll event of that type is fired. Otherwise, + * "DOMMouseScroll" is used. + * + * If the axis is specified, it must be one of "horizontal" or "vertical". If not specified, + * "vertical" is used. + * + * 'delta' is the amount to scroll by (can be positive or negative). It must + * be specified. + * + * 'hasPixels' specifies whether kHasPixels should be set in the scrollFlags. + * + * 'isMomentum' specifies whether kIsMomentum should be set in the scrollFlags. + * + * aWindow is optional, and defaults to the current window object. + */ +function synthesizeMouseScroll(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) +{ + var utils = _getDOMWindowUtils(aWindow); + + if (utils) { + // See nsMouseScrollFlags in nsGUIEvent.h + const kIsVertical = 0x02; + const kIsHorizontal = 0x04; + const kHasPixels = 0x08; + const kIsMomentum = 0x40; + + var button = aEvent.button || 0; + var modifiers = _parseModifiers(aEvent); + + var rect = aTarget.getBoundingClientRect(); + + var left = rect.left; + var top = rect.top; + + var type = (("type" in aEvent) && aEvent.type) || "DOMMouseScroll"; + var axis = aEvent.axis || "vertical"; + var scrollFlags = (axis == "horizontal") ? kIsHorizontal : kIsVertical; + if (aEvent.hasPixels) { + scrollFlags |= kHasPixels; + } + if (aEvent.isMomentum) { + scrollFlags |= kIsMomentum; + } + utils.sendMouseScrollEvent(type, left + aOffsetX, top + aOffsetY, button, + scrollFlags, aEvent.delta, modifiers); + } +} + +function _computeKeyCodeFromChar(aChar) +{ + if (aChar.length != 1) { + return 0; + } + const nsIDOMKeyEvent = Components.interfaces.nsIDOMKeyEvent; + if (aChar >= 'a' && aChar <= 'z') { + return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0); + } + if (aChar >= 'A' && aChar <= 'Z') { + return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0); + } + if (aChar >= '0' && aChar <= '9') { + return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0); + } + // returns US keyboard layout's keycode + switch (aChar) { + case '~': + case '`': + return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE; + case '!': + return nsIDOMKeyEvent.DOM_VK_1; + case '@': + return nsIDOMKeyEvent.DOM_VK_2; + case '#': + return nsIDOMKeyEvent.DOM_VK_3; + case '$': + return nsIDOMKeyEvent.DOM_VK_4; + case '%': + return nsIDOMKeyEvent.DOM_VK_5; + case '^': + return nsIDOMKeyEvent.DOM_VK_6; + case '&': + return nsIDOMKeyEvent.DOM_VK_7; + case '*': + return nsIDOMKeyEvent.DOM_VK_8; + case '(': + return nsIDOMKeyEvent.DOM_VK_9; + case ')': + return nsIDOMKeyEvent.DOM_VK_0; + case '-': + case '_': + return nsIDOMKeyEvent.DOM_VK_SUBTRACT; + case '+': + case '=': + return nsIDOMKeyEvent.DOM_VK_EQUALS; + case '{': + case '[': + return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET; + case '}': + case ']': + return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET; + case '|': + case '\\': + return nsIDOMKeyEvent.DOM_VK_BACK_SLASH; + case ':': + case ';': + return nsIDOMKeyEvent.DOM_VK_SEMICOLON; + case '\'': + case '"': + return nsIDOMKeyEvent.DOM_VK_QUOTE; + case '<': + case ',': + return nsIDOMKeyEvent.DOM_VK_COMMA; + case '>': + case '.': + return nsIDOMKeyEvent.DOM_VK_PERIOD; + case '?': + case '/': + return nsIDOMKeyEvent.DOM_VK_SLASH; + default: + return 0; + } +} + +/** + * isKeypressFiredKey() returns TRUE if the given key should cause keypress + * event when widget handles the native key event. Otherwise, FALSE. + * + * aDOMKeyCode should be one of consts of nsIDOMKeyEvent::DOM_VK_*, or a key + * name begins with "VK_", or a character. + */ +function isKeypressFiredKey(aDOMKeyCode) +{ + if (typeof(aDOMKeyCode) == "string") { + if (aDOMKeyCode.indexOf("VK_") == 0) { + aDOMKeyCode = KeyEvent["DOM_" + aDOMKeyCode]; + if (!aDOMKeyCode) { + throw "Unknown key: " + aDOMKeyCode; + } + } else { + // If the key generates a character, it must cause a keypress event. + return true; + } + } + switch (aDOMKeyCode) { + case KeyEvent.DOM_VK_SHIFT: + case KeyEvent.DOM_VK_CONTROL: + case KeyEvent.DOM_VK_ALT: + case KeyEvent.DOM_VK_CAPS_LOCK: + case KeyEvent.DOM_VK_NUM_LOCK: + case KeyEvent.DOM_VK_SCROLL_LOCK: + case KeyEvent.DOM_VK_META: + return false; + default: + return true; + } +} + +/** + * Synthesize a key event. It is targeted at whatever would be targeted by an + * actual keypress by the user, typically the focused element. + * + * aKey should be either a character or a keycode starting with VK_ such as + * VK_ENTER. + * + * aEvent is an object which may contain the properties: + * shiftKey, ctrlKey, altKey, metaKey, accessKey, type, location + * + * Sets one of KeyboardEvent.DOM_KEY_LOCATION_* to location. Otherwise, + * DOMWindowUtils will choose good location from the keycode. + * + * If the type is specified, a key event of that type is fired. Otherwise, + * a keydown, a keypress and then a keyup event are fired in sequence. + * + * aWindow is optional, and defaults to the current window object. + */ +function synthesizeKey(aKey, aEvent, aWindow) +{ + var utils = _getDOMWindowUtils(aWindow); + if (utils) { + var keyCode = 0, charCode = 0; + if (aKey.indexOf("VK_") == 0) { + keyCode = KeyEvent["DOM_" + aKey]; + if (!keyCode) { + throw "Unknown key: " + aKey; + } + } else { + charCode = aKey.charCodeAt(0); + keyCode = _computeKeyCodeFromChar(aKey.charAt(0)); + } + + var modifiers = _parseModifiers(aEvent); + var flags = 0; + if (aEvent.location != undefined) { + switch (aEvent.location) { + case KeyboardEvent.DOM_KEY_LOCATION_STANDARD: + flags |= utils.KEY_FLAG_LOCATION_STANDARD; + break; + case KeyboardEvent.DOM_KEY_LOCATION_LEFT: + flags |= utils.KEY_FLAG_LOCATION_LEFT; + break; + case KeyboardEvent.DOM_KEY_LOCATION_RIGHT: + flags |= utils.KEY_FLAG_LOCATION_RIGHT; + break; + case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD: + flags |= utils.KEY_FLAG_LOCATION_NUMPAD; + break; + case KeyboardEvent.DOM_KEY_LOCATION_MOBILE: + flags |= utils.KEY_FLAG_LOCATION_MOBILE; + break; + case KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK: + flags |= utils.KEY_FLAG_LOCATION_JOYSTICK; + break; + } + } + + if (!("type" in aEvent) || !aEvent.type) { + // Send keydown + (optional) keypress + keyup events. + var keyDownDefaultHappened = + utils.sendKeyEvent("keydown", keyCode, 0, modifiers, flags); + if (isKeypressFiredKey(keyCode)) { + if (!keyDownDefaultHappened) { + flags |= utils.KEY_FLAG_PREVENT_DEFAULT; + } + utils.sendKeyEvent("keypress", keyCode, charCode, modifiers, flags); + } + utils.sendKeyEvent("keyup", keyCode, 0, modifiers, flags); + } else if (aEvent.type == "keypress") { + // Send standalone keypress event. + utils.sendKeyEvent(aEvent.type, keyCode, charCode, modifiers, flags); + } else { + // Send other standalone event than keypress. + utils.sendKeyEvent(aEvent.type, keyCode, 0, modifiers, flags); + } + } +} + +var _gSeenEvent = false; + +/** + * Indicate that an event with an original target of aExpectedTarget and + * a type of aExpectedEvent is expected to be fired, or not expected to + * be fired. + */ +function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName) +{ + if (!aExpectedTarget || !aExpectedEvent) + return null; + + _gSeenEvent = false; + + var type = (aExpectedEvent.charAt(0) == "!") ? + aExpectedEvent.substring(1) : aExpectedEvent; + var eventHandler = function(event) { + var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget && + event.type == type); + is(epassed, true, aTestName + " " + type + " event target " + (_gSeenEvent ? "twice" : "")); + _gSeenEvent = true; + }; + + aExpectedTarget.addEventListener(type, eventHandler, false); + return eventHandler; +} + +/** + * Check if the event was fired or not. The event handler aEventHandler + * will be removed. + */ +function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName) +{ + if (aEventHandler) { + var expectEvent = (aExpectedEvent.charAt(0) != "!"); + var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1); + aExpectedTarget.removeEventListener(type, aEventHandler, false); + var desc = type + " event"; + if (!expectEvent) + desc += " not"; + is(_gSeenEvent, expectEvent, aTestName + " " + desc + " fired"); + } + + _gSeenEvent = false; +} + +/** + * Similar to synthesizeMouse except that a test is performed to see if an + * event is fired at the right target as a result. + * + * aExpectedTarget - the expected originalTarget of the event. + * aExpectedEvent - the expected type of the event, such as 'select'. + * aTestName - the test name when outputing results + * + * To test that an event is not fired, use an expected type preceded by an + * exclamation mark, such as '!select'. This might be used to test that a + * click on a disabled element doesn't fire certain events for instance. + * + * aWindow is optional, and defaults to the current window object. + */ +function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent, + aExpectedTarget, aExpectedEvent, aTestName, + aWindow) +{ + var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName); + synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow); + _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName); +} + +/** + * Similar to synthesizeKey except that a test is performed to see if an + * event is fired at the right target as a result. + * + * aExpectedTarget - the expected originalTarget of the event. + * aExpectedEvent - the expected type of the event, such as 'select'. + * aTestName - the test name when outputing results + * + * To test that an event is not fired, use an expected type preceded by an + * exclamation mark, such as '!select'. + * + * aWindow is optional, and defaults to the current window object. + */ +function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent, + aTestName, aWindow) +{ + var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName); + synthesizeKey(key, aEvent, aWindow); + _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName); +} + +function disableNonTestMouseEvents(aDisable) +{ + var domutils = _getDOMWindowUtils(); + domutils.disableNonTestMouseEvents(aDisable); +} + +function _getDOMWindowUtils(aWindow) +{ + if (!aWindow) { + aWindow = window; + } + + // we need parent.SpecialPowers for: + // layout/base/tests/test_reftests_with_caret.html + // chrome: toolkit/content/tests/chrome/test_findbar.xul + // chrome: toolkit/content/tests/chrome/test_popup_anchor.xul + if ("SpecialPowers" in window && window.SpecialPowers != undefined) { + return SpecialPowers.getDOMWindowUtils(aWindow); + } + if ("SpecialPowers" in parent && parent.SpecialPowers != undefined) { + return parent.SpecialPowers.getDOMWindowUtils(aWindow); + } + + //TODO: this is assuming we are in chrome space + return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor). + getInterface(Components.interfaces.nsIDOMWindowUtils); +} + +// Must be synchronized with nsIDOMWindowUtils. +// XXXhumph: already defined in SpecialPowers extension +//const COMPOSITION_ATTR_RAWINPUT = 0x02; +//const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03; +//const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04; +//const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05; + +/** + * Synthesize a composition event. + * + * @param aEvent The composition event information. This must + * have |type| member. The value must be + * "compositionstart", "compositionend" or + * "compositionupdate". + * And also this may have |data| and |locale| which + * would be used for the value of each property of + * the composition event. Note that the data would + * be ignored if the event type were + * "compositionstart". + * @param aWindow Optional (If null, current |window| will be used) + */ +function synthesizeComposition(aEvent, aWindow) +{ + var utils = _getDOMWindowUtils(aWindow); + if (!utils) { + return; + } + + utils.sendCompositionEvent(aEvent.type, aEvent.data ? aEvent.data : "", + aEvent.locale ? aEvent.locale : ""); +} +/** + * Synthesize a text event. + * + * @param aEvent The text event's information, this has |composition| + * and |caret| members. |composition| has |string| and + * |clauses| members. |clauses| must be array object. Each + * object has |length| and |attr|. And |caret| has |start| and + * |length|. See the following tree image. + * + * aEvent + * +-- composition + * | +-- string + * | +-- clauses[] + * | +-- length + * | +-- attr + * +-- caret + * +-- start + * +-- length + * + * Set the composition string to |composition.string|. Set its + * clauses information to the |clauses| array. + * + * When it's composing, set the each clauses' length to the + * |composition.clauses[n].length|. The sum of the all length + * values must be same as the length of |composition.string|. + * Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the + * |composition.clauses[n].attr|. + * + * When it's not composing, set 0 to the + * |composition.clauses[0].length| and + * |composition.clauses[0].attr|. + * + * Set caret position to the |caret.start|. It's offset from + * the start of the composition string. Set caret length to + * |caret.length|. If it's larger than 0, it should be wide + * caret. However, current nsEditor doesn't support wide + * caret, therefore, you should always set 0 now. + * + * @param aWindow Optional (If null, current |window| will be used) + */ +function synthesizeText(aEvent, aWindow) +{ + var utils = _getDOMWindowUtils(aWindow); + if (!utils) { + return; + } + + if (!aEvent.composition || !aEvent.composition.clauses || + !aEvent.composition.clauses[0]) { + return; + } + + var firstClauseLength = aEvent.composition.clauses[0].length; + var firstClauseAttr = aEvent.composition.clauses[0].attr; + var secondClauseLength = 0; + var secondClauseAttr = 0; + var thirdClauseLength = 0; + var thirdClauseAttr = 0; + if (aEvent.composition.clauses[1]) { + secondClauseLength = aEvent.composition.clauses[1].length; + secondClauseAttr = aEvent.composition.clauses[1].attr; + if (aEvent.composition.clauses[2]) { + thirdClauseLength = aEvent.composition.clauses[2].length; + thirdClauseAttr = aEvent.composition.clauses[2].attr; + } + } + + var caretStart = -1; + var caretLength = 0; + if (aEvent.caret) { + caretStart = aEvent.caret.start; + caretLength = aEvent.caret.length; + } + + utils.sendTextEvent(aEvent.composition.string, + firstClauseLength, firstClauseAttr, + secondClauseLength, secondClauseAttr, + thirdClauseLength, thirdClauseAttr, + caretStart, caretLength); +} + +/** + * Synthesize a query selected text event. + * + * @param aWindow Optional (If null, current |window| will be used) + * @return An nsIQueryContentEventResult object. If this failed, + * the result might be null. + */ +function synthesizeQuerySelectedText(aWindow) +{ + var utils = _getDOMWindowUtils(aWindow); + if (!utils) { + return null; + } + + return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0); +} diff --git a/test/ui/test.js b/test/ui/test.js new file mode 100644 index 00000000..d448a08f --- /dev/null +++ b/test/ui/test.js @@ -0,0 +1,47 @@ +document.addEventListener( "DOMContentLoaded", function( e ){ + + Butter.init({ + config: 'ui-config.json', + ready: function( butter ){ + var media = butter.media[ 0 ]; + + function start(){ + butterReady = true; + + var track = media.addTrack( "Track1" ); + media.addTrack( "Track" + Math.random() ); + media.addTrack( "Track" + Math.random() ); + + var event = track.addTrackEvent({ + type: "text", + popcornOptions: { + start: 0, + end: 3, + text: "test", + target: "Area1" + } + }); + + butter.tracks[ 2 ].addTrackEvent({ + type: "text", + popcornOptions: { + start: 1, + end: 2, + target: "Area2" + } + }); + + /** + * QUnit Tests for UI go here. + **/ + test( "window.SpecialPowers", function(){ + ok( "SpecialPowers" in window, "SpecialPowers extension is installed." ); + }); + + } + + media.onReady( start ); + } + }); + +}, false ); diff --git a/test/ui/tool-tip.html b/test/ui/tool-tip.html new file mode 100644 index 00000000..518a87c1 --- /dev/null +++ b/test/ui/tool-tip.html @@ -0,0 +1,73 @@ + + + + Butter Tool-Tip Tests + + + + + + + + + + + + + + + + + + + +
                                                                                                                                                                  +
                                                                                                                                                                  +

                                                                                                                                                                  Area 1

                                                                                                                                                                  +
                                                                                                                                                                  +
                                                                                                                                                                  +
                                                                                                                                                                  +

                                                                                                                                                                  Area 2

                                                                                                                                                                  +
                                                                                                                                                                  +
                                                                                                                                                                  + +
                                                                                                                                                                  +

                                                                                                                                                                  Butter Tool-Tip Test Template

                                                                                                                                                                  +

                                                                                                                                                                  +
                                                                                                                                                                  +

                                                                                                                                                                  +
                                                                                                                                                                    +
                                                                                                                                                                    + + diff --git a/test/ui/tool-tip.js b/test/ui/tool-tip.js new file mode 100644 index 00000000..220d35b7 --- /dev/null +++ b/test/ui/tool-tip.js @@ -0,0 +1,84 @@ +document.addEventListener( "DOMContentLoaded", function( e ){ + + Butter.init({ + config: 'ui-config.json', + ready: function( butter ){ + var media = butter.media[ 0 ]; + + function start(){ + test( "ToolTip Tests", function() { + + var fooTip, + newMessage = "Changing the ToolTip Text", + parent = document.querySelector( "a.butter-btn.butter-editor-header-media" ), + newParent = document.querySelector( "a.butter-btn.butter-editor-header-popcorn" ); + + Butter.ToolTip.create({ + name: "foo-tip", + element: parent, + message: "This is a ToolTip", + left: "25%", + top: "55%" + }) + + fooTip = Butter.ToolTip.get( "foo-tip" ); + + //ToolTip parent + ok( fooTip.parent, "The ToolTip has a Parent element" ); + strictEqual( fooTip.parent, parent, "The ToolTip has the correct Parent" ); + fooTip.parent = newParent; + strictEqual( fooTip.parent, newParent, "The parent reference was updated" ); + ok( !parent.querySelector( "div.butter-tooltip" ), "The old parent does not contain a tooltip div" ); + ok( newParent.querySelector( "div.butter-tooltip" ), "The new parent got the tooltip div"); + + // Check that parent was given abs or rel positioning + notStrictEqual( [ "absolute", "relative" ].indexOf( getComputedStyle( fooTip.parent ).getPropertyValue( "position" ) ), -1, "The parent element was given absolute or relative positioning" ); + + // ToolTip name property + equal( fooTip.name, "foo-tip", "Butter.ToolTip.get returned the correctly named tooltip." ); + fooTip.name = "This-Should-Not-Work"; + equal( fooTip.name, "foo-tip", "tooltip name property cannot be modified" ); + + // ToolTip message property + equal( fooTip.message, "This is a ToolTip", "Message was assigned correctly." ); + equal( fooTip.tooltipElement.innerHTML, "This is a ToolTip", "The ToolTip element was given the correct message text" ); + fooTip.message = newMessage; + equal( fooTip.message, newMessage, "Message text was changed" ); + equal( fooTip.tooltipElement.innerHTML, newMessage, "The ToolTip's element innerHTML was changed" ); + + // ToolTip top property + equal( fooTip.top, "55%", "The ToolTip was assigned the correct top value" ); + equal( fooTip.tooltipElement.style.top, "55%", "The ToolTip's element was given the correct top value" ); + fooTip.top = "25%"; + equal( fooTip.top, "25%", "The ToolTip's top was updated" ); + equal( fooTip.tooltipElement.style.top, "25%", "The updated top was applied to the ToolTip element" ); + + // ToolTip left property + equal( fooTip.left, "25%", "The ToolTip was assigned the correct left value" ); + equal( fooTip.tooltipElement.style.left, "25%", "The ToolTip's element was given the correct left value" ); + fooTip.left = "55%"; + equal( fooTip.left, "55%", "The ToolTip's left was updated" ); + equal( fooTip.tooltipElement.style.left, "55%", "The updated left was applied to the ToolTip element" ); + + // ToolTip hidden property + equal( fooTip.hidden, true, "When not passed a hidden value, it defaults to true" ); + ok( !fooTip.tooltipElement.classList.contains( "tooltip-on" ), "The tooltip element does not contain the 'tooltip-on' class" ); + fooTip.hidden = false; + equal( fooTip.hidden, false, "The hidden property was updated" ); + ok( fooTip.tooltipElement.classList.contains( "tooltip-on" ), "The tooltip element contains the 'tooltip-on' class now" ); + + // ToolTip hover property + equal( fooTip.hover, true, "When not passed a hover value, it defaults to true" ); + ok( !fooTip.tooltipElement.classList.contains( "tooltip-no-hover" ), "The tooltip element does not contain the 'tooltip-no-hover' class" ); + fooTip.hover = false; + equal( fooTip.hover, false, "The hover property was updated" ); + ok( fooTip.tooltipElement.classList.contains( "tooltip-no-hover" ), "The tooltip element contains the 'tooltip-no-hover' class now" ); + + }); + } + + media.onReady( start ); + } + }); + +}, false ); diff --git a/test/ui/ui-config.json b/test/ui/ui-config.json new file mode 100644 index 00000000..2ad1bb15 --- /dev/null +++ b/test/ui/ui-config.json @@ -0,0 +1,52 @@ +{ + "name": "ui-config", + "baseDir": "../../", + "cssRenderClientSide": false, + "snapshotHTMLOnReady": false, + "scrapePage": true, + "title": "Popcorn Maker", + "ui": { + "enabled": true, + "onLeaveDialog": true, + "trackEventHighlight": "click" + }, + "makeVideoURLsUnique": true, + "mediaDefaults": { + "frameAnimation": true + }, + "trackEvent": { + "defaultDuration": 5 + }, + "plugin": { + "plugins": [ + { + "type": "text", + "path": "../../templates/assets/plugins/text/popcorn.text.js" + }, + { + "type": "image", + "path": "../../templates/assets/plugins/image/popcorn.image.js" + }, + { + "name": "googlemap", + "type": "googlemap", + "path": "../../templates/assets/plugins/googlemap/popcorn.googlemap.js" + } + ], + "defaults": [ + "text", + "image", + "googlemap" + ] + }, + "dirs": { + "popcorn-js": "../../external/popcorn-js/", + "css": "../../css/", + "dialogs": "../../dialogs/", + "resources": "../../resources/" + }, + "icons": { + "default": "popcorn-icon.png", + "image": "image-icon.png" + } +} diff --git a/test/uri/index.html b/test/uri/index.html new file mode 100644 index 00000000..944d0809 --- /dev/null +++ b/test/uri/index.html @@ -0,0 +1,223 @@ + + + + Butter Test Suite [URI] + + + + + + + + +

                                                                                                                                                                    Butter API Test Suite [URI]

                                                                                                                                                                    +

                                                                                                                                                                    +
                                                                                                                                                                    +

                                                                                                                                                                    +
                                                                                                                                                                      + + diff --git a/tests.conf b/tests.conf new file mode 100644 index 00000000..e5d50299 --- /dev/null +++ b/tests.conf @@ -0,0 +1,67 @@ +{ + "core": { + "media-create-object": "test/core/core-media/media-create-object.html", + "media-functionality": "test/core/core-media/media-functionality.html", + "media-manipulate-object": "test/core/core-media/media-manipulate-object.html", + "media-multiple-urls": "test/core/core-media/media-multiple-urls.html", + "media-multiple-urls-databuttersource": "test/core/core-media/media-multiple-urls-databuttersource.html", + "media-own-tracks": "test/core/core-media/media-own-tracks.html", + "media-targets": "test/core/core-media/media-targets.html", + "media-max-plugin-zindex": "test/core/core-media/media-max-plugin-zindex.html", + "debug": "test/core/core-debug/index.html", + "dependency-auto-load-data": "test/core/core-dependency/dependency-auto-load-data.html", + "dependency-load-css": "test/core/core-dependency/dependency-load-css.html", + "dependency-load-test-script": "test/core/core-dependency/dependency-load-test-script.html", + "dependency-plugin-loading": "test/core/core-dependency/dependency-plugin-loading.html", + "dialog": "test/core/core-dialog/index.html", + "import-export-html-escape": "test/core/core-import_export/import-export-html-escape.html", + "import-export-import-export": "test/core/core-import_export/import-export.html", + "core-player-basic": "test/core/core-player/player-basic.html", + "core-player-strange-element": "test/core/core-player/player-strange-element.html", + "core-scripts-existence-execution": "test/core/core-scripts/scripts-existence-execution.html", + "core-scripts-none-callbacks": "test/core/core-scripts/scripts-none-callbacks.html", + "target-creation-removal": "test/core/core-target/target-creation-removal.html", + "target-serialization": "test/core/core-target/target-serialization.html", + "track-creation": "test/core/core-track/track-creation.html", + "track-management": "test/core/core-track/track-management.html", + "trackevent-creation": "test/core/core-trackevent/trackevent-creation.html", + "trackevent-defaults": "test/core/core-trackevent/trackevent-defaults.html", + "trackevent-getTrackEvents": "test/core/core-trackevent/trackevent-getTrackEvents.html", + "trackevent-getTrackEventsByType": "test/core/core-trackevent/trackevent-getTrackEventsByType.html", + "trackevent-management": "test/core/core-trackevent/trackevent-management.html", + "trackevent-remove-add-constituent": "test/core/core-trackevent/trackevent-remove-add-constituent.html" + }, + "modules": { + "editor-basic-usage": "test/editor/editor-basic-usage.html", + "editor-custom": "test/editor/editor-custom.html", + "editor-createStartEndInputs": "test/editor/editor-createStartEndInputs.html", + "eventmanager": "test/eventmanager/index.html", + "observer": "test/observer/index.html", + "config": "test/config/index.html", + "logger": "test/logger/index.html", + "popcorn-wrapper": "test/popcorn-wrapper/index.html", + "media-addTrack": "test/media/media-addTrack.html", + "media-findTrackWithTrackEventId": "test/media/media-findTrackWithTrackEventId.html", + "media-generatePopcornString": "test/media/media-generatePopcornString.html", + "media-getManifest": "test/media/media-getManifest.html", + "media-getTrackById": "test/media/media-getTrackById.html", + "media-removeTrack": "test/media/media-removeTrack.html", + "page-prepare": "test/page/page-prepare.html", + "page-addPlayerType": "test/page/page-addPlayerType.html", + "page-scrape": "test/page/page-scrape.html", + "trackevent-updateValid": "test/trackevent/trackevent-updateValid.html", + "trackevent-updateInvalid": "test/trackevent/trackevent-updateInvalid.html", + "trackevent-previewText": "test/trackevent/trackevent-preview-text.html", + "track-addTrackEvent": "test/track/track-addTrackEvent.html", + "track-deselectEvents": "test/track/track-deselectEvents.html", + "track-getTrackEventById": "test/track/track-getTrackEventById.html", + "track-getTrackEventByName": "test/track/track-getTrackEventByName.html", + "track-removeTrackEvent": "test/track/track-removeTrackEvent.html", + "track-removeEmptyTrack": "test/track/track-removeEmptyTrack.html", + "UI-ToolTip": "test/ui/tool-tip.html" + }, + "utils": { + "uri": "test/uri/index.html", + "timecode": "test/timecode/index.html" + } +} diff --git a/tools/almond.js b/tools/almond.js new file mode 100644 index 00000000..613854a0 --- /dev/null +++ b/tools/almond.js @@ -0,0 +1,277 @@ +/** + * almond 0.0.3 Copyright (c) 2011, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/almond for details + */ +/*jslint strict: false, plusplus: false */ +/*global setTimeout: false */ + +var requirejs, require, define; +(function (undef) { + + var defined = {}, + waiting = {}, + aps = [].slice, + main, req; + + if (typeof define === "function") { + //If a define is already in play via another AMD loader, + //do not overwrite. + return; + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @returns {String} normalized name + */ + function normalize(name, baseName) { + //Adjust any relative paths. + if (name && name.charAt(0) === ".") { + //If have a base name, try to normalize against it, + //otherwise, assume it is a top-level require that will + //be relative to baseUrl in the end. + if (baseName) { + //Convert baseName to array, and lop off the last part, + //so that . matches that "directory" and not name of the baseName's + //module. For instance, baseName of "one/two/three", maps to + //"one/two/three.js", but we want the directory, "one/two" for + //this normalization. + baseName = baseName.split("/"); + baseName = baseName.slice(0, baseName.length - 1); + + name = baseName.concat(name.split("/")); + + //start trimDots + var i, part; + for (i = 0; (part = name[i]); i++) { + if (part === ".") { + name.splice(i, 1); + i -= 1; + } else if (part === "..") { + if (i === 1 && (name[2] === '..' || name[0] === '..')) { + //End of the line. Keep at least one non-dot + //path segment at the front so it can be mapped + //correctly to disk. Otherwise, there is likely + //no path mapping for a path starting with '..'. + //This can still fail, but catches the most reasonable + //uses of .. + break; + } else if (i > 0) { + name.splice(i - 1, 2); + i -= 2; + } + } + } + //end trimDots + + name = name.join("/"); + } + } + return name; + } + + function makeRequire(relName, forceSync) { + return function () { + //A version of a require function that passes a moduleName + //value for items that may need to + //look up paths relative to the moduleName + return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync])); + }; + } + + function makeNormalize(relName) { + return function (name) { + return normalize(name, relName); + }; + } + + function makeLoad(depName) { + return function (value) { + defined[depName] = value; + }; + } + + function callDep(name) { + if (waiting.hasOwnProperty(name)) { + var args = waiting[name]; + delete waiting[name]; + main.apply(undef, args); + } + return defined[name]; + } + + /** + * Makes a name map, normalizing the name, and using a plugin + * for normalization if necessary. Grabs a ref to plugin + * too, as an optimization. + */ + function makeMap(name, relName) { + var prefix, plugin, + index = name.indexOf('!'); + + if (index !== -1) { + prefix = normalize(name.slice(0, index), relName); + name = name.slice(index + 1); + plugin = callDep(prefix); + + //Normalize according + if (plugin && plugin.normalize) { + name = plugin.normalize(name, makeNormalize(relName)); + } else { + name = normalize(name, relName); + } + } else { + name = normalize(name, relName); + } + + //Using ridiculous property names for space reasons + return { + f: prefix ? prefix + '!' + name : name, //fullName + n: name, + p: plugin + }; + } + + main = function (name, deps, callback, relName) { + var args = [], + usingExports, + cjsModule, depName, i, ret, map; + + //Use name if no relName + if (!relName) { + relName = name; + } + + //Call the callback to define the module, if necessary. + if (typeof callback === 'function') { + + //Default to require, exports, module if no deps if + //the factory arg has any arguments specified. + if (!deps.length && callback.length) { + deps = ['require', 'exports', 'module']; + } + + //Pull out the defined dependencies and pass the ordered + //values to the callback. + for (i = 0; i < deps.length; i++) { + map = makeMap(deps[i], relName); + depName = map.f; + + //Fast path CommonJS standard dependencies. + if (depName === "require") { + args[i] = makeRequire(name); + } else if (depName === "exports") { + //CommonJS module spec 1.1 + args[i] = defined[name] = {}; + usingExports = true; + } else if (depName === "module") { + //CommonJS module spec 1.1 + cjsModule = args[i] = { + id: name, + uri: '', + exports: defined[name] + }; + } else if (defined.hasOwnProperty(depName) || waiting.hasOwnProperty(depName)) { + args[i] = callDep(depName); + } else if (map.p) { + map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); + args[i] = defined[depName]; + } else { + throw name + ' missing ' + depName; + } + } + + ret = callback.apply(defined[name], args); + + if (name) { + //If setting exports via "module" is in play, + //favor that over return value and exports. After that, + //favor a non-undefined return value over exports use. + if (cjsModule && cjsModule.exports !== undef) { + defined[name] = cjsModule.exports; + } else if (!usingExports) { + //Use the return value from the function. + defined[name] = ret; + } + } + } else if (name) { + //May just be an object definition for the module. Only + //worry about defining if have a module name. + defined[name] = callback; + } + }; + + requirejs = req = function (deps, callback, relName, forceSync) { + if (typeof deps === "string") { + + //Just return the module wanted. In this scenario, the + //deps arg is the module name, and second arg (if passed) + //is just the relName. + //Normalize module name, if it contains . or .. + return callDep(makeMap(deps, callback).f); + } else if (!deps.splice) { + //deps is a config object, not an array. + //Drop the config stuff on the ground. + if (callback.splice) { + //callback is an array, which means it is a dependency list. + //Adjust args if there are dependencies + deps = callback; + callback = arguments[2]; + } else { + deps = []; + } + } + + //Simulate async callback; + if (forceSync) { + main(undef, deps, callback, relName); + } else { + setTimeout(function () { + main(undef, deps, callback, relName); + }, 15); + } + + return req; + }; + + /** + * Just drops the config on the floor, but returns req in case + * the config return value is used. + */ + req.config = function () { + return req; + }; + + /** + * Export require as a global, but only if it does not already exist. + */ + if (!require) { + require = req; + } + + define = function (name, deps, callback) { + + //This module may not have dependencies + if (!deps.splice) { + //deps is not an array, so probably means + //an object literal or factory function for + //the value. Adjust args. + callback = deps; + deps = []; + } + + if (define.unordered) { + waiting[name] = [name, deps, callback]; + } else { + main(name, deps, callback); + } + }; + + define.amd = { + jQuery: true + }; +}()); diff --git a/tools/beautify.sh b/tools/beautify.sh new file mode 100755 index 00000000..c23489cb --- /dev/null +++ b/tools/beautify.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +cd ../ +if [ -n "$1" ]; then + files=$1 +else + DIRS=( src editors test templates dialogs cornfield config ) + files=`find ${DIRS[*]} -name '*.js'` +fi +touch tmp.txt +for word in $files +do + python tools/jsbeautifier.py -s 2 -j -o tmp.txt --extra-expr-spacing=1 $word + rm $word + mv tmp.txt $word + echo beautified $word + bash tools/regex.sh $word + echo regex ran on $word +done diff --git a/tools/build.js b/tools/build.js new file mode 100644 index 00000000..d9e47d17 --- /dev/null +++ b/tools/build.js @@ -0,0 +1,47 @@ +/** + * Build profile for butter. Replaces the use of requirejs with an AMD + * loader shim, almond.js, since the built file does not need to use + * all of requirejs. + */ +({ + // Where to find the module names listed below. + baseUrl: '../src', + + paths: { + 'text': '../external/require/text' + }, + + // Use has branch trimming in the build to remove the document.write + // code in src/butter.js after a minification is done. + has: { + 'source-config': false + }, + + // Target the AMD loader shim as the main module to optimize, + // so it shows up first in the built file, + // since the butter modules use the define/require APIs that the almond + // provides. Path is relative to baseUrl. + name: '../tools/almond', + + // Strip extra licenses + preserveLicenseComments: false, + + // Files to include along with almond. Their nested dependencies will also be + // included. Subsystems are listed explicitly because butter-src.js does not + // have explicit dependencies for them, but uses them on demand. Also, + // butter.js references butter-src in a document.write string, so it will + // not be found by the AST analysis done in the optimizer. + include: [ + 'butter', + 'main' + ], + + // Wraps Butter in a closure and adds license information + wrap: { + startFile: '../tools/wrap.start', + endFile: '../tools/wrap.end' + }, + + // The built butter.js file for use by web sites. + out: '../dist/src/butter.js' +}) diff --git a/tools/classlist-shim.js b/tools/classlist-shim.js new file mode 100644 index 00000000..53e129d6 --- /dev/null +++ b/tools/classlist-shim.js @@ -0,0 +1,122 @@ + +(function(){ + +/** + * Cross-browser full element.classList implementation for IE9 and friends. + * 2011-06-15 + * + * By Eli Grey, http://purl.eligrey.com/github/classList.js/blob/master/classList.js + * Public Domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + **/ + +if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) { + (function (view) { + "use strict"; + + var classListProp = "classList", + protoProp = "prototype", + elemCtrProto = (view.HTMLElement || view.Element)[protoProp], + objCtr = Object, + strTrim = String[protoProp].trim || function () { + return this.replace(/^\s+|\s+$/g, ""); + }, + arrIndexOf = Array[protoProp].indexOf || function (item) { + var i = 0, + len = this.length; + for (; i < len; i++) { + if (i in this && this[i] === item) { + return i; + } + } + return -1; + }, + // Vendors: please allow content code to instantiate DOMExceptions + DOMEx = function (type, message) { + this.name = type; + this.code = DOMException[type]; + this.message = message; + }, + checkTokenAndGetIndex = function (classList, token) { + if (token === "") { + throw new DOMEx("SYNTAX_ERR", "An invalid or illegal string was specified"); + } + if (/\s/.test(token)) { + throw new DOMEx("INVALID_CHARACTER_ERR", "String contains an invalid character"); + } + return arrIndexOf.call(classList, token); + }, + ClassList = function (elem) { + var trimmedClasses = strTrim.call(elem.className), + classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [], + i = 0, + len = classes.length; + for (; i < len; i++) { + this.push(classes[i]); + } + this._updateClassName = function () { + elem.className = this.toString(); + }; + }, + classListProto = ClassList[protoProp] = [], + classListGetter = function () { + return new ClassList(this); + }; + + // Most DOMException implementations don't allow calling DOMException's toString() + // on non-DOMExceptions. Error's toString() is sufficient here. + DOMEx[protoProp] = Error[protoProp]; + classListProto.item = function (i) { + return this[i] || null; + }; + classListProto.contains = function (token) { + token += ""; + return checkTokenAndGetIndex(this, token) !== -1; + }; + classListProto.add = function (token) { + token += ""; + if (checkTokenAndGetIndex(this, token) === -1) { + this.push(token); + this._updateClassName(); + } + }; + classListProto.remove = function (token) { + token += ""; + var index = checkTokenAndGetIndex(this, token); + if (index !== -1) { + this.splice(index, 1); + this._updateClassName(); + } + }; + classListProto.toggle = function (token) { + token += ""; + if (checkTokenAndGetIndex(this, token) === -1) { + this.add(token); + } else { + this.remove(token); + } + }; + classListProto.toString = function () { + return this.join(" "); + }; + + if (objCtr.defineProperty) { + var classListPropDesc = { + get: classListGetter, + enumerable: true, + configurable: true + }; + try { + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } catch (ex) { // IE 8 doesn't support enumerable:true + if (ex.number === -0x7FF5EC54) { + classListPropDesc.enumerable = false; + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } + } + } else if (objCtr[protoProp].__defineGetter__) { + elemCtrProto.__defineGetter__(classListProp, classListGetter); + } + }(self)); +} +}()); \ No newline at end of file diff --git a/tools/dox.py b/tools/dox.py new file mode 100644 index 00000000..0ae1471d --- /dev/null +++ b/tools/dox.py @@ -0,0 +1,652 @@ +#!/usr/bin/env python + +import sys +import getopt +import re +import json + +# Tokenizer stolen from Einar Lielmanis et al.'s python version of jsbeautifier, +# MIT licence, enjoy. + +def formatJSON(input): + if input == None: + return None + return json.dumps(input) + +def formatMD(input): + if input == None: + return None + + def process_tree(subtree, tabs): + next_tabs = tabs + 1 + output = "" + if 'name' in subtree and subtree['name'] != None: + tab_str = "" + for i in range(0, tabs): + tab_str += "#" + + if 'type' in subtree: + output += tab_str + ' ' + str(subtree['type']) + ": " + str(subtree['name']) + "\n" + else: + output += tab_str + ' ' + str(subtree['name']) + "\n" + + if 'doc' in subtree: + output += str(subtree['doc']) + "\n" + + if 'type' in subtree and 'properties' in subtree: + output += "\n" + + signature = 'Usage: __' + str(subtree['name']) + '(' + params = "" + for prop in subtree['properties']: + if 'name' in prop: + output += '* __' + prop['type'] + '__ _' + prop['name'] + '_ ' + if 'data-type' in prop: + output += " _(" + prop['data_type'] + ")_" + if 'description' in prop: + output += ": " + prop['description'] + "\n" + elif 'type' in prop: + if 'description' in prop: + output += '* __' + prop['type'] + "__: " + prop['description'] + "\n" + + if 'name' in prop: + params += prop['name'] + ', ' + signature += params[:-2] + ')__' + + if not subtree['type'] in ["Module", "Property"]: + output += "\n" + signature + "\n" + + output += "\n" + + else: + next_tabs = tabs + + for c in subtree['children']: + output += process_tree(c, next_tabs) + + return output + + return process_tree(input, 1) + +def formatTXT(input): + if input == None: + return None + + def process_tree(subtree, tabs): + next_tabs = tabs + 1 + output = "" + if subtree['name'] != None: + tab_str = "" + for i in range(0, tabs): + tab_str += " " + output += tab_str + if subtree['type'] != None: + output += subtree['type'] + ": " + subtree['name'] + "\n" + else: + output += subtree['name'] + "\n" + + if subtree['doc'] != None: + output += tab_str + subtree['doc'] + "\n" + + if subtree['properties'] != None: + for prop in subtree['properties']: + if 'name' in prop: + output += tab_str + '@' + prop['type'] + ' ' + prop['name'] + if prop['data_type'] != None: + output += " [" + prop['data_type'] + "]" + if prop['description'] != None: + output += ": " + prop['description'] + "\n" + elif 'type' in prop: + if prop['description'] != None: + output += tab_str + '@' + prop['type'] + ": " + prop['description'] + "\n" + output += "\n" + else: + next_tabs = tabs + + for c in subtree['children']: + output += process_tree(c, next_tabs) + + return output + + return process_tree(input, 0) + + +__processors = { + 'json': formatJSON, + 'txt': formatTXT, + 'md': formatMD +}; + +def process(string, opts = None): + b = Dox() + try: + processor = opts['processor'] + except: + processor = formatJSON + + return processor(b.beautify(string, opts)) + + +def process_file(file_name, opts = None): + + if file_name == '-': # stdin + f = sys.stdin + else: + f = open(file_name) + + b = Dox() + + try: + processor = opts['processor'] + except: + processor = formatJSON + + return processor(b.process(''.join(f.readlines()), opts)) + + +def usage(): + + print("""dox + +Usage: dox.py [options] + + can be "-", which means stdin. + defaults to stdout + +Input options: + + -i, --stdin read input from stdin + +Output options: + + -p, --processor=PROCESSOR specify an intermediary processor for data + -o, --outfile=FILE specify a file to output to (default stdout) + +Rarely needed options: + + -h, --help, --usage prints this help statement. + +"""); + + +class TreeProperties(): + def __init__(self): + self.description = None + self.type = None + self.name = None + self.properties = [] + + +class Tree: + def __init__(self, root=None): + self.root = root + self.children = [] + self.properties = TreeProperties() + + def clean(self): + self.children = [child for child in self.children if not child.clean() ] + return self.null_check() + + def export(self): + children_export = [] + for child in self.children: + if not child.null_check(): + children_export.append(child.export()) + + p = self.properties + return { + 'name': p.name, + 'type': p.type, + 'properties': p.properties, + 'doc': p.description, + 'children': children_export + } + + def null_check(self): + p = self.properties + return len(self.children) == 0 and p.description == None and p.type == None and p.name == None and len(p.properties) == 0 + +class Flags: + def __init__(self): + self.in_html_comment = False + +class Dox: + + def __init__(self, opts = None): + + self.opts = opts + self.blank_state() + + def blank_state(self): + + # internal flags + self.flags = Flags() + self.flag_store = [] + + + self.last_word = '' # last TK_WORD seen + self.last_type = 'TK_START_EXPR' # last token type + self.last_text = '' # last token text + self.last_last_text = '' # pre-last token text + + self.input = None + + self.whitespace = ["\n", "\r", "\t", " "] + self.wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$' + self.digits = '0123456789' + self.punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::'.split(' '); + + + self.current_tree = Tree() + self.root_tree = self.current_tree + + self.saved_properties = TreeProperties() + + global parser_pos + parser_pos = 0 + + + def process(self, s, opts = None ): + + if opts != None: + self.opts = opts + + self.blank_state() + + while s and s[0] in [' ', '\t']: + self.preindent_string += s[0] + s = s[1:] + + self.input = s + + parser_pos = 0 + while True: + token_text, token_type = self.get_next_token() + #print (token_text, token_type, self.flags.mode) + if token_type == 'TK_EOF': + break + + handlers = { + 'TK_START_EXPR': self.handle_start_expr, + 'TK_END_EXPR': self.handle_end_expr, + 'TK_START_BLOCK': self.handle_start_block, + 'TK_END_BLOCK': self.handle_end_block, + 'TK_WORD': self.handle_word, + 'TK_SEMICOLON': self.handle_semicolon, + 'TK_STRING': self.handle_string, + 'TK_EQUALS': self.handle_equals, + 'TK_OPERATOR': self.handle_operator, + 'TK_BLOCK_COMMENT': self.handle_block_comment, + 'TK_INLINE_COMMENT': self.handle_inline_comment, + 'TK_COMMENT': self.handle_comment, + 'TK_UNKNOWN': self.handle_unknown, + } + + handlers[token_type](token_text) + + self.last_last_text = self.last_text + self.last_type = token_type + self.last_text = token_text + + self.root_tree.clean() + export_data = self.root_tree.export() + + if self.root_tree.null_check(): + export_data = None + + return export_data + + + def get_next_token(self): + + global parser_pos + + self.n_newlines = 0 + + if parser_pos >= len(self.input): + return '', 'TK_EOF' + + self.wanted_newline = False; + c = self.input[parser_pos] + parser_pos += 1 + + while c in self.whitespace: + if parser_pos >= len(self.input): + return '', 'TK_EOF' + + c = self.input[parser_pos] + parser_pos += 1 + + if c in self.wordchar: + if parser_pos < len(self.input): + while self.input[parser_pos] in self.wordchar: + c = c + self.input[parser_pos] + parser_pos += 1 + if parser_pos == len(self.input): + break + + # small and surprisingly unugly hack for 1E-10 representation + if parser_pos != len(self.input) and self.input[parser_pos] in '+-' \ + and re.match('^[0-9]+[Ee]$', c): + + sign = self.input[parser_pos] + parser_pos += 1 + t = self.get_next_token() + c += sign + t[0] + return c, 'TK_WORD' + + if c == 'in': # in is an operator, need to hack + return c, 'TK_OPERATOR' + + return c, 'TK_WORD' + + if c in '([': + return c, 'TK_START_EXPR' + + if c in ')]': + return c, 'TK_END_EXPR' + + if c == '{': + return c, 'TK_START_BLOCK' + + if c == '}': + return c, 'TK_END_BLOCK' + + if c == ';': + return c, 'TK_SEMICOLON' + + if c == '/': + comment = '' + inline_comment = True + comment_mode = 'TK_INLINE_COMMENT' + if self.input[parser_pos] == '*': # peek /* .. */ comment + parser_pos += 1 + if parser_pos < len(self.input): + while not (self.input[parser_pos] == '*' and \ + parser_pos + 1 < len(self.input) and \ + self.input[parser_pos + 1] == '/')\ + and parser_pos < len(self.input): + c = self.input[parser_pos] + comment += c + if c in '\r\n': + comment_mode = 'TK_BLOCK_COMMENT' + parser_pos += 1 + if parser_pos >= len(self.input): + break + parser_pos += 2 + return '/*' + comment + '*/', comment_mode + if self.input[parser_pos] == '/': # peek // comment + comment = c + while self.input[parser_pos] not in '\r\n': + comment += self.input[parser_pos] + parser_pos += 1 + if parser_pos >= len(self.input): + break + parser_pos += 1 + return comment, 'TK_COMMENT' + + + + if c == "'" or c == '"' or \ + (c == '/' and ((self.last_type == 'TK_WORD' and self.last_text in ['return', 'do']) or \ + (self.last_type in ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR', + 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON']))): + sep = c + esc = False + resulting_string = c + in_char_class = False + + if parser_pos < len(self.input): + if sep == '/': + # handle regexp + in_char_class = False + while esc or in_char_class or self.input[parser_pos] != sep: + resulting_string += self.input[parser_pos] + if not esc: + esc = self.input[parser_pos] == '\\' + if self.input[parser_pos] == '[': + in_char_class = True + elif self.input[parser_pos] == ']': + in_char_class = False + else: + esc = False + parser_pos += 1 + if parser_pos >= len(self.input): + # incomplete regex when end-of-file reached + # bail out with what has received so far + return resulting_string, 'TK_STRING' + else: + # handle string + while esc or self.input[parser_pos] != sep: + resulting_string += self.input[parser_pos] + if not esc: + esc = self.input[parser_pos] == '\\' + else: + esc = False + parser_pos += 1 + if parser_pos >= len(self.input): + # incomplete string when end-of-file reached + # bail out with what has received so far + return resulting_string, 'TK_STRING' + + + parser_pos += 1 + resulting_string += sep + if sep == '/': + # regexps may have modifiers /regexp/MOD, so fetch those too + while parser_pos < len(self.input) and self.input[parser_pos] in self.wordchar: + resulting_string += self.input[parser_pos] + parser_pos += 1 + return resulting_string, 'TK_STRING' + + if c == '#': + + # she-bang + if len(self.output) == 0 and len(self.input) > 1 and self.input[parser_pos] == '!': + resulting_string = c + while parser_pos < len(self.input) and c != '\n': + c = self.input[parser_pos] + resulting_string += c + parser_pos += 1 + return self.get_next_token() + + + # Spidermonkey-specific sharp variables for circular references + # https://developer.mozilla.org/En/Sharp_variables_in_JavaScript + # http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935 + sharp = '#' + if parser_pos < len(self.input) and self.input[parser_pos] in self.digits: + while True: + c = self.input[parser_pos] + sharp += c + parser_pos += 1 + if parser_pos >= len(self.input) or c == '#' or c == '=': + break + if c == '#' or parser_pos >= len(self.input): + pass + elif self.input[parser_pos] == '[' and self.input[parser_pos + 1] == ']': + sharp += '[]' + parser_pos += 2 + elif self.input[parser_pos] == '{' and self.input[parser_pos + 1] == '}': + sharp += '{}' + parser_pos += 2 + return sharp, 'TK_WORD' + + if c == '<' and self.input[parser_pos - 1 : parser_pos + 3] == '': + self.flags.in_html_comment = False + parser_pos += 2 + return '-->', 'TK_COMMENT' + + if c in self.punct: + while parser_pos < len(self.input) and c + self.input[parser_pos] in self.punct: + c += self.input[parser_pos] + parser_pos += 1 + if parser_pos >= len(self.input): + break + if c == '=': + return c, 'TK_EQUALS' + else: + return c, 'TK_OPERATOR' + return c, 'TK_UNKNOWN' + + def push_tree(self): + new_tree = Tree(self.current_tree) + self.current_tree.children.append(new_tree) + self.current_tree = new_tree + + def pop_tree(self): + self.current_tree = self.current_tree.root + + def handle_start_expr(self, token_text): + pass + + + def handle_end_expr(self, token_text): + pass + + + def handle_start_block(self, token_text): + self.push_tree() + self.current_tree.properties = self.saved_properties + self.saved_properties = TreeProperties() + + def handle_end_block(self, token_text): + self.pop_tree() + + + def handle_word(self, token_text): + pass + + + def handle_semicolon(self, token_text): + pass + + + def handle_string(self, token_text): + pass + + + def handle_equals(self, token_text): + pass + + + def handle_operator(self, token_text): + pass + + + def handle_block_comment(self, token_text): + self.saved_properties = TreeProperties() + result = re.search('\* ([A-Z]\w+):\s(\w+)', token_text) + if result and result.group(1) and result.group(2): + self.saved_properties.type = result.group(1) + self.saved_properties.name = result.group(2) + else: + return + + lines = token_text.split('\n')[2:] + description = [] + while len(lines) > 0: + line = lines[0] + trimmed = line.strip()[1:].strip() + + m = re.search('@(\w+):\s(.+)', trimmed) + if m: + self.saved_properties.properties.append({ + 'type': m.group(1), + 'description': m.group(2) + }) + + else: + m = re.search('@(\w+)\s(\w+):\s(.+)', trimmed) + if m: + self.saved_properties.properties.append({ + 'type': m.group(1), + 'name': m.group(2), + 'description': m.group(3) + }) + + else: + m = re.search('@(\w+)\s\{(\w+)\}\s(\w+):\s(.+)', trimmed) + if m: + self.saved_properties.properties.append({ + 'type': m.group(1), + 'data_type': m.group(2), + 'name': m.group(3), + 'description': m.group(4) + }) + elif not trimmed in ['', '/']: + description.append(trimmed) + + lines = lines[1:] + + self.saved_properties.description = '\n'.join(description) + + + def handle_inline_comment(self, token_text): + pass + + + def handle_comment(self, token_text): + pass + + + def handle_unknown(self, token_text): + pass + +def main(): + + argv = sys.argv[1:] + + try: + opts, args = getopt.getopt(argv, "o:t:i:h", ['outfile=', 'type=', 'help', 'usage', 'stdin']) + except getopt.GetoptError: + usage() + sys.exit(2) + + options = { + 'processor': None + } + + file = None + outfile = 'stdout' + + if len(args) == 1: + file = args[0] + + options['processor'] = formatJSON + + for opt, arg in opts: + if opt in ('--outfile', '-o'): + outfile = arg + elif opt in ('--type', '-t'): + try: + options['processor'] = __processors[ arg ] + except KeyError: + pass + elif opt in ('--stdin', '-i'): + file = arg + elif opt in ('--help', '--usage', '--h'): + return usage() + + if file == None: + return usage() + else: + output = process_file(file, options) + if output != None: + if outfile == 'stdout': + print(output) + else: + f = open(outfile, 'w') + f.write(output + '\n') + f.close() + + +if __name__ == "__main__": + main() + + diff --git a/tools/embed.js b/tools/embed.js new file mode 100644 index 00000000..7a5b4a54 --- /dev/null +++ b/tools/embed.js @@ -0,0 +1,35 @@ +/** + * Build profile for Butter Embed. Replaces the use of requirejs with an AMD + * loader shim, almond.js, since the built file does not need to use + * all of requirejs. + */ +({ + // Where to find the module names listed below. + baseUrl: '../src', + + paths: { + 'text': '../external/require/text' + }, + + // Target the AMD loader shim as the main module to optimize, + // so it shows up first in the built file, + // since the embed modules use the define/require APIs that the almond + // provides. Path is relative to baseUrl. + name: '../tools/almond', + + // Strip extra licenses + preserveLicenseComments: false, + + // Files to include along with almond. Their nested dependencies will also be + // included. + include: [ 'embed' ], + + // Wraps Butter in a closure and adds license information + wrap: { + startFile: '../tools/wrap.start', + endFile: '../tools/wrap.end' + }, + + // The built embed.js file for use by web sites. + out: '../dist/src/embed.js' +}) diff --git a/tools/jsbeautifier.py b/tools/jsbeautifier.py new file mode 100644 index 00000000..824e62b7 --- /dev/null +++ b/tools/jsbeautifier.py @@ -0,0 +1,1119 @@ +#!/usr/bin/env python + +import sys +import getopt +import re + +# +# Originally written by Einar Lielmanis et al., +# Conversion to python by Einar Lielmanis, einar@jsbeautifier.org, +# MIT licence, enjoy. +# +# Python is not my native language, feel free to push things around. +# +# Use either from command line (script displays its usage when run +# without any parameters), +# +# +# or, alternatively, use it as a module: +# +# import jsbeautifier +# res = jsbeautifier.beautify('your javascript string') +# res = jsbeautifier.beautify_file('some_file.js') +# +# you may specify some options: +# +# opts = jsbeautifier.default_options() +# opts.indent_size = 2 +# res = jsbeautifier.beautify('some javascript', opts) +# +# +# Here are the available options: (read source) + + +class BeautifierOptions: + def __init__(self): + self.indent_size = 4 + self.indent_char = ' ' + self.preserve_newlines = True + self.max_preserve_newlines = 10. + self.jslint_happy = False + self.brace_style = 'collapse' + self.keep_array_indentation = False + self.extra_expr_spacing = None + + + + def __repr__(self): + return \ +"""indent_size = %d +indent_char = [%s] +preserve_newlines = %s +max_preserve_newlines = %d +jslint_happy = %s +brace_style = %s +keep_array_indentation = %s +extra_expr_spacing = %d +""" % ( self.indent_size, + self.indent_char, + self.preserve_newlines, + self.max_preserve_newlines, + self.jslint_happy, + self.brace_style, + self.keep_array_indentation, + self.extra_expr_spacing + ) + + +class BeautifierFlags: + def __init__(self, mode): + self.previous_mode = 'BLOCK' + self.mode = mode + self.var_line = False + self.var_line_tainted = False + self.var_line_reindented = False + self.in_html_comment = False + self.if_line = False + self.in_case = False + self.eat_next_space = False + self.indentation_baseline = -1 + self.indentation_level = 0 + self.ternary_depth = 0 + + +def default_options(): + return BeautifierOptions() + + +def beautify(string, opts = default_options() ): + b = Beautifier() + return b.beautify(string, opts) + + +def beautify_file(file_name, opts = default_options() ): + + if file_name == '-': # stdin + f = sys.stdin + else: + f = open(file_name) + + b = Beautifier() + return b.beautify(''.join(f.readlines()), opts) + + +def usage(): + + print("""Javascript beautifier (http://jsbeautifier.org/) + +Usage: jsbeautifier.py [options] + + can be "-", which means stdin. + defaults to stdout + +Input options: + + -i, --stdin read input from stdin + +Output options: + + -s, --indent-size=NUMBER indentation size. (default 4). + -c, --indent-char=CHAR character to indent with. (default space). + -d, --disable-preserve-newlines do not preserve existing line breaks. + -j, --jslint-happy more jslint-compatible output + -b, --brace-style=collapse brace style (collapse, expand, end-expand) + -k, --keep-array-indentation keep array indentation. + -e, --extra-expr-spacing=NUMBER insert extra spacing before and after expressions + -o, --outfile=FILE specify a file to output to (default stdout) + +Rarely needed options: + + -l, --indent-level=NUMBER initial indentation level. (default 0). + + -h, --help, --usage prints this help statement. + +"""); + + + + + + +class Beautifier: + + def __init__(self, opts = default_options() ): + + self.opts = opts + self.blank_state() + + def blank_state(self): + + # internal flags + self.flags = BeautifierFlags('BLOCK') + self.flag_store = [] + self.wanted_newline = False + self.just_added_newline = False + self.do_block_just_closed = False + + + self.indent_string = self.opts.indent_char * self.opts.indent_size + self.preindent_string = '' + self.last_word = '' # last TK_WORD seen + self.last_type = 'TK_START_EXPR' # last token type + self.last_text = '' # last token text + self.last_last_text = '' # pre-last token text + + self.input = None + self.output = [] # formatted javascript gets built here + + self.whitespace = ["\n", "\r", "\t", " "] + self.wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$' + self.digits = '0123456789' + self.punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::'.split(' '); + + + # Words which always should start on a new line + self.line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',') + self.limited_line_starters = 'if,for,while,function'.split(',') + self.set_mode('BLOCK') + + global parser_pos + parser_pos = 0 + + + def beautify(self, s, opts = None ): + + if opts != None: + self.opts = opts + + + if self.opts.brace_style not in ['expand', 'collapse', 'end-expand']: + raise(Exception('opts.brace_style must be "expand", "collapse" or "end-expand".')) + + self.blank_state() + + while s and s[0] in [' ', '\t']: + self.preindent_string += s[0] + s = s[1:] + + self.input = s + + parser_pos = 0 + while True: + token_text, token_type = self.get_next_token() + #print (token_text, token_type, self.flags.mode) + if token_type == 'TK_EOF': + break + + handlers = { + 'TK_START_EXPR': self.handle_start_expr, + 'TK_END_EXPR': self.handle_end_expr, + 'TK_START_BLOCK': self.handle_start_block, + 'TK_END_BLOCK': self.handle_end_block, + 'TK_WORD': self.handle_word, + 'TK_SEMICOLON': self.handle_semicolon, + 'TK_STRING': self.handle_string, + 'TK_EQUALS': self.handle_equals, + 'TK_OPERATOR': self.handle_operator, + 'TK_BLOCK_COMMENT': self.handle_block_comment, + 'TK_INLINE_COMMENT': self.handle_inline_comment, + 'TK_COMMENT': self.handle_comment, + 'TK_UNKNOWN': self.handle_unknown, + } + + handlers[token_type](token_text) + + self.last_last_text = self.last_text + self.last_type = token_type + self.last_text = token_text + + sweet_code = self.preindent_string + re.sub('[\n ]+$', '', ''.join(self.output)) + return sweet_code + + + + def trim_output(self, eat_newlines = False): + while len(self.output) \ + and ( + self.output[-1] == ' '\ + or self.output[-1] == self.indent_string \ + or self.output[-1] == self.preindent_string \ + or (eat_newlines and self.output[-1] in ['\n', '\r'])): + self.output.pop() + + + def is_array(self, mode): + return mode in ['[EXPRESSION]', '[INDENDED-EXPRESSION]'] + + + def is_expression(self, mode): + return mode in ['[EXPRESSION]', '[INDENDED-EXPRESSION]', '(EXPRESSION)'] + + + def append_newline_forced(self): + old_array_indentation = self.opts.keep_array_indentation + self.opts.keep_array_indentation = False + self.append_newline() + self.opts.keep_array_indentation = old_array_indentation + + def append_newline(self, ignore_repeated = True): + + self.flags.eat_next_space = False; + + if self.opts.keep_array_indentation and self.is_array(self.flags.mode): + return + + self.flags.if_line = False; + self.trim_output(); + + if len(self.output) == 0: + # no newline on start of file + return + + if self.output[-1] != '\n' or not ignore_repeated: + self.just_added_newline = True + self.output.append('\n') + + if self.preindent_string: + self.output.append(self.preindent_string) + + for i in range(self.flags.indentation_level): + self.output.append(self.indent_string) + + if self.flags.var_line and self.flags.var_line_reindented: + self.output.append(self.indent_string) + + + def append(self, s): + if s == ' ': + # make sure only single space gets drawn + if self.flags.eat_next_space: + self.flags.eat_next_space = False + elif len(self.output) and self.output[-1] not in [' ', '\n', self.indent_string]: + self.output.append(' ') + else: + self.just_added_newline = False + self.flags.eat_next_space = False + self.output.append(s) + + + def indent(self): + self.flags.indentation_level = self.flags.indentation_level + 1 + + + def remove_indent(self): + if len(self.output) and self.output[-1] in [self.indent_string, self.preindent_string]: + self.output.pop() + + + def set_mode(self, mode): + + prev = BeautifierFlags('BLOCK') + + if self.flags: + self.flag_store.append(self.flags) + prev = self.flags + + self.flags = BeautifierFlags(mode) + + if len(self.flag_store) == 1: + self.flags.indentation_level = 0 + else: + self.flags.indentation_level = prev.indentation_level + if prev.var_line and prev.var_line_reindented: + self.flags.indentation_level = self.flags.indentation_level + 1 + self.flags.previous_mode = prev.mode + + + def restore_mode(self): + self.do_block_just_closed = self.flags.mode == 'DO_BLOCK' + if len(self.flag_store) > 0: + self.flags = self.flag_store.pop() + + + def get_next_token(self): + + global parser_pos + + self.n_newlines = 0 + + if parser_pos >= len(self.input): + return '', 'TK_EOF' + + self.wanted_newline = False; + c = self.input[parser_pos] + parser_pos += 1 + + keep_whitespace = self.opts.keep_array_indentation and self.is_array(self.flags.mode) + + if keep_whitespace: + # slight mess to allow nice preservation of array indentation and reindent that correctly + # first time when we get to the arrays: + # var a = [ + # ....'something' + # we make note of whitespace_count = 4 into flags.indentation_baseline + # so we know that 4 whitespaces in original source match indent_level of reindented source + # + # and afterwards, when we get to + # 'something, + # .......'something else' + # we know that this should be indented to indent_level + (7 - indentation_baseline) spaces + + whitespace_count = 0 + while c in self.whitespace: + if c == '\n': + self.trim_output() + self.output.append('\n') + self.just_added_newline = True + whitespace_count = 0 + elif c == '\t': + whitespace_count += 4 + elif c == '\r': + pass + else: + whitespace_count += 1 + + if parser_pos >= len(self.input): + return '', 'TK_EOF' + + c = self.input[parser_pos] + parser_pos += 1 + + if self.flags.indentation_baseline == -1: + + self.flags.indentation_baseline = whitespace_count + + if self.just_added_newline: + for i in range(self.flags.indentation_level + 1): + self.output.append(self.indent_string) + + if self.flags.indentation_baseline != -1: + for i in range(whitespace_count - self.flags.indentation_baseline): + self.output.append(' ') + + else: # not keep_whitespace + while c in self.whitespace: + if c == '\n': + if self.opts.max_preserve_newlines == 0 or self.opts.max_preserve_newlines > self.n_newlines: + self.n_newlines += 1 + + if parser_pos >= len(self.input): + return '', 'TK_EOF' + + c = self.input[parser_pos] + parser_pos += 1 + + if self.opts.preserve_newlines and self.n_newlines > 1: + for i in range(self.n_newlines): + self.append_newline(i == 0) + self.just_added_newline = True + + self.wanted_newline = self.n_newlines > 0 + + + if c in self.wordchar: + if parser_pos < len(self.input): + while self.input[parser_pos] in self.wordchar: + c = c + self.input[parser_pos] + parser_pos += 1 + if parser_pos == len(self.input): + break + + # small and surprisingly unugly hack for 1E-10 representation + if parser_pos != len(self.input) and self.input[parser_pos] in '+-' \ + and re.match('^[0-9]+[Ee]$', c): + + sign = self.input[parser_pos] + parser_pos += 1 + t = self.get_next_token() + c += sign + t[0] + return c, 'TK_WORD' + + if c == 'in': # in is an operator, need to hack + return c, 'TK_OPERATOR' + + if self.wanted_newline and \ + self.last_type != 'TK_OPERATOR' and\ + self.last_type != 'TK_EQUALS' and\ + not self.flags.if_line and \ + (self.opts.preserve_newlines or self.last_text != 'var'): + self.append_newline() + + return c, 'TK_WORD' + + if c in '([': + return c, 'TK_START_EXPR' + + if c in ')]': + return c, 'TK_END_EXPR' + + if c == '{': + return c, 'TK_START_BLOCK' + + if c == '}': + return c, 'TK_END_BLOCK' + + if c == ';': + return c, 'TK_SEMICOLON' + + if c == '/': + comment = '' + inline_comment = True + comment_mode = 'TK_INLINE_COMMENT' + if self.input[parser_pos] == '*': # peek /* .. */ comment + parser_pos += 1 + if parser_pos < len(self.input): + while not (self.input[parser_pos] == '*' and \ + parser_pos + 1 < len(self.input) and \ + self.input[parser_pos + 1] == '/')\ + and parser_pos < len(self.input): + c = self.input[parser_pos] + comment += c + if c in '\r\n': + comment_mode = 'TK_BLOCK_COMMENT' + parser_pos += 1 + if parser_pos >= len(self.input): + break + parser_pos += 2 + return '/*' + comment + '*/', comment_mode + if self.input[parser_pos] == '/': # peek // comment + comment = c + while self.input[parser_pos] not in '\r\n': + comment += self.input[parser_pos] + parser_pos += 1 + if parser_pos >= len(self.input): + break + parser_pos += 1 + if self.wanted_newline: + self.append_newline() + return comment, 'TK_COMMENT' + + + + if c == "'" or c == '"' or \ + (c == '/' and ((self.last_type == 'TK_WORD' and self.last_text in ['return', 'do']) or \ + (self.last_type in ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR', + 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON']))): + sep = c + esc = False + resulting_string = c + in_char_class = False + + if parser_pos < len(self.input): + if sep == '/': + # handle regexp + in_char_class = False + while esc or in_char_class or self.input[parser_pos] != sep: + resulting_string += self.input[parser_pos] + if not esc: + esc = self.input[parser_pos] == '\\' + if self.input[parser_pos] == '[': + in_char_class = True + elif self.input[parser_pos] == ']': + in_char_class = False + else: + esc = False + parser_pos += 1 + if parser_pos >= len(self.input): + # incomplete regex when end-of-file reached + # bail out with what has received so far + return resulting_string, 'TK_STRING' + else: + # handle string + while esc or self.input[parser_pos] != sep: + resulting_string += self.input[parser_pos] + if not esc: + esc = self.input[parser_pos] == '\\' + else: + esc = False + parser_pos += 1 + if parser_pos >= len(self.input): + # incomplete string when end-of-file reached + # bail out with what has received so far + return resulting_string, 'TK_STRING' + + + parser_pos += 1 + resulting_string += sep + if sep == '/': + # regexps may have modifiers /regexp/MOD, so fetch those too + while parser_pos < len(self.input) and self.input[parser_pos] in self.wordchar: + resulting_string += self.input[parser_pos] + parser_pos += 1 + return resulting_string, 'TK_STRING' + + if c == '#': + + # she-bang + if len(self.output) == 0 and len(self.input) > 1 and self.input[parser_pos] == '!': + resulting_string = c + while parser_pos < len(self.input) and c != '\n': + c = self.input[parser_pos] + resulting_string += c + parser_pos += 1 + self.output.append(resulting_string.strip() + "\n") + self.append_newline() + return self.get_next_token() + + + # Spidermonkey-specific sharp variables for circular references + # https://developer.mozilla.org/En/Sharp_variables_in_JavaScript + # http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935 + sharp = '#' + if parser_pos < len(self.input) and self.input[parser_pos] in self.digits: + while True: + c = self.input[parser_pos] + sharp += c + parser_pos += 1 + if parser_pos >= len(self.input) or c == '#' or c == '=': + break + if c == '#' or parser_pos >= len(self.input): + pass + elif self.input[parser_pos] == '[' and self.input[parser_pos + 1] == ']': + sharp += '[]' + parser_pos += 2 + elif self.input[parser_pos] == '{' and self.input[parser_pos + 1] == '}': + sharp += '{}' + parser_pos += 2 + return sharp, 'TK_WORD' + + if c == '<' and self.input[parser_pos - 1 : parser_pos + 3] == '': + self.flags.in_html_comment = False + parser_pos += 2 + if self.wanted_newline: + self.append_newline() + return '-->', 'TK_COMMENT' + + if c in self.punct: + while parser_pos < len(self.input) and c + self.input[parser_pos] in self.punct: + c += self.input[parser_pos] + parser_pos += 1 + if parser_pos >= len(self.input): + break + if c == '=': + return c, 'TK_EQUALS' + else: + return c, 'TK_OPERATOR' + return c, 'TK_UNKNOWN' + + + + def handle_start_expr(self, token_text): + if token_text == '[': + if self.last_type == 'TK_WORD' or self.last_text == ')': + if self.last_text in self.line_starters: + self.append(' ') + self.set_mode('(EXPRESSION)') + self.append(token_text) + return + + if self.flags.mode in ['[EXPRESSION]', '[INDENTED-EXPRESSION]']: + if self.last_last_text == ']' and self.last_text == ',': + # ], [ goes to a new line + if self.flags.mode == '[EXPRESSION]': + self.flags.mode = '[INDENTED-EXPRESSION]' + if not self.opts.keep_array_indentation: + self.indent() + self.set_mode('[EXPRESSION]') + if not self.opts.keep_array_indentation: + self.append_newline() + elif self.last_text == '[': + if self.flags.mode == '[EXPRESSION]': + self.flags.mode = '[INDENTED-EXPRESSION]' + if not self.opts.keep_array_indentation: + self.indent() + self.set_mode('[EXPRESSION]') + + if not self.opts.keep_array_indentation: + self.append_newline() + else: + self.set_mode('[EXPRESSION]') + else: + self.set_mode('[EXPRESSION]') + else: + self.set_mode('(EXPRESSION)') + + + if self.last_text == ';' or self.last_type == 'TK_START_BLOCK': + self.append_newline() + elif self.last_type in ['TK_END_EXPR', 'TK_START_EXPR', 'TK_END_BLOCK'] or self.last_text == '.': + # do nothing on (( and )( and ][ and ]( and .( + pass + elif self.last_type not in ['TK_WORD', 'TK_OPERATOR']: + self.append(' ') + elif self.last_word == 'function' or self.last_word == 'typeof': + # function() vs function (), typeof() vs typeof () + if self.opts.jslint_happy: + self.append(' ') + elif self.last_text in self.line_starters or self.last_text == 'catch': + self.append(' ') + + if self.opts.extra_expr_spacing and self.last_type in ['TK_START_EXPR', 'TK_WORD'] and self.last_text in self.limited_line_starters: + self.append(self.opts.extra_expr_spacing) + + self.append(token_text) + + + def handle_end_expr(self, token_text): + if token_text == ']': + if self.opts.keep_array_indentation: + if self.last_text == '}': + self.remove_indent() + self.append(token_text) + self.restore_mode() + return + else: + if self.flags.mode == '[INDENTED-EXPRESSION]': + if self.last_text == ']': + self.restore_mode() + self.append_newline() + self.append(token_text) + return + self.restore_mode() + + if self.opts.extra_expr_spacing and self.last_type in ['TK_WORD', 'TK_STRING', 'TK_END_EXPR' ]: + self.append(self.opts.extra_expr_spacing) + + self.append(token_text) + + + def handle_start_block(self, token_text): + if self.last_word == 'do': + self.set_mode('DO_BLOCK') + else: + self.set_mode('BLOCK') + + if self.opts.brace_style == 'expand': + if self.last_type != 'TK_OPERATOR': + if self.last_text in ['return', '=']: + self.append(' ') + else: + self.append_newline(True) + + self.append(token_text) + self.indent() + else: + if self.last_type not in ['TK_OPERATOR', 'TK_START_EXPR']: + if self.last_type == 'TK_START_BLOCK': + self.append_newline() + else: + self.append(' ') + else: + # if TK_OPERATOR or TK_START_EXPR + if self.is_array(self.flags.previous_mode) and self.last_text == ',': + if self.last_last_text == '}': + self.append(' ') + else: + self.append_newline() + self.indent() + self.append(token_text) + + + def handle_end_block(self, token_text): + self.restore_mode() + if self.opts.brace_style == 'expand': + if self.last_text != '{': + self.append_newline() + else: + if self.last_type == 'TK_START_BLOCK': + if self.just_added_newline: + self.remove_indent() + else: + # {} + self.trim_output() + else: + if self.is_array(self.flags.mode) and self.opts.keep_array_indentation: + self.opts.keep_array_indentation = False + self.append_newline() + self.opts.keep_array_indentation = True + else: + self.append_newline() + + self.append(token_text) + + + def handle_word(self, token_text): + if self.do_block_just_closed: + self.append(' ') + self.append(token_text) + self.append(' ') + self.do_block_just_closed = False + return + + if token_text == 'function': + + if self.flags.var_line: + self.flags.var_line_reindented = True + if (self.just_added_newline or self.last_text == ';') and self.last_text != '{': + # make sure there is a nice clean space of at least one blank line + # before a new function definition + have_newlines = self.n_newlines + if not self.just_added_newline: + have_newlines = 0 + if not self.opts.preserve_newlines: + have_newlines = 1 + for i in range(2 - have_newlines): + self.append_newline(False) + + if token_text in ['case', 'default']: + if self.last_text == ':': + self.remove_indent() + else: + self.flags.indentation_level -= 1 + self.append_newline() + self.flags.indentation_level += 1 + self.append(token_text) + self.flags.in_case = True + return + + prefix = 'NONE' + + if self.last_type == 'TK_END_BLOCK': + if token_text not in ['else', 'catch', 'finally']: + prefix = 'NEWLINE' + else: + if self.opts.brace_style in ['expand', 'end-expand']: + prefix = 'NEWLINE' + else: + prefix = 'SPACE' + self.append(' ') + elif self.last_type == 'TK_SEMICOLON' and self.flags.mode in ['BLOCK', 'DO_BLOCK']: + prefix = 'NEWLINE' + elif self.last_type == 'TK_SEMICOLON' and self.is_expression(self.flags.mode): + prefix = 'SPACE' + elif self.last_type == 'TK_STRING': + prefix = 'NEWLINE' + elif self.last_type == 'TK_WORD': + if self.last_text == 'else': + # eat newlines between ...else *** some_op... + # won't preserve extra newlines in this place (if any), but don't care that much + self.trim_output(True); + prefix = 'SPACE' + elif self.last_type == 'TK_START_BLOCK': + prefix = 'NEWLINE' + elif self.last_type == 'TK_END_EXPR': + self.append(' ') + prefix = 'NEWLINE' + + if self.flags.if_line and self.last_type == 'TK_END_EXPR': + self.flags.if_line = False + + if token_text in self.line_starters: + if self.last_text == 'else': + prefix = 'SPACE' + else: + prefix = 'NEWLINE' + + if token_text in ['else', 'catch', 'finally']: + if self.last_type != 'TK_END_BLOCK' \ + or self.opts.brace_style == 'expand' \ + or self.opts.brace_style == 'end-expand': + self.append_newline() + else: + self.trim_output(True) + self.append(' ') + elif prefix == 'NEWLINE': + if token_text == 'function' and (self.last_type == 'TK_START_EXPR' or self.last_text in '=,'): + # no need to force newline on "function" - + # (function... + pass + elif token_text == 'function' and self.last_text == 'new': + self.append(' ') + elif self.last_text in ['return', 'throw']: + # no newline between return nnn + self.append(' ') + elif self.last_type != 'TK_END_EXPR': + if (self.last_type != 'TK_START_EXPR' or token_text != 'var') and self.last_text != ':': + # no need to force newline on VAR - + # for (var x = 0... + if token_text == 'if' and self.last_word == 'else' and self.last_text != '{': + self.append(' ') + else: + self.flags.var_line = False + self.flags.var_line_reindented = False + self.append_newline() + elif token_text in self.line_starters and self.last_text != ')': + self.flags.var_line = False + self.flags.var_line_reindented = False + self.append_newline() + elif self.is_array(self.flags.mode) and self.last_text == ',' and self.last_last_text == '}': + self.append_newline() # }, in lists get a newline + elif prefix == 'SPACE': + self.append(' ') + + if self.opts.extra_expr_spacing and self.last_type == 'TK_START_EXPR': + self.append(self.opts.extra_expr_spacing) + + self.append(token_text) + self.last_word = token_text + + if token_text == 'var': + self.flags.var_line = True + self.flags.var_line_reindented = False + self.flags.var_line_tainted = False + + + if token_text == 'if': + self.flags.if_line = True + + if token_text == 'else': + self.flags.if_line = False + + + def handle_semicolon(self, token_text): + self.append(token_text) + self.flags.var_line = False + self.flags.var_line_reindented = False + if self.flags.mode == 'OBJECT': + # OBJECT mode is weird and doesn't get reset too well. + self.flags.mode = 'BLOCK' + + + def handle_string(self, token_text): + if self.last_type in ['TK_START_BLOCK', 'TK_END_BLOCK', 'TK_SEMICOLON']: + self.append_newline() + elif self.last_type == 'TK_WORD': + self.append(' ') + + if self.opts.extra_expr_spacing and self.last_type in ['TK_START_EXPR']: + self.append(self.opts.extra_expr_spacing) + + # Try to replace \x-encoded characters with their readable equivalent, + # if it is possible (e.g. '\x41\x42\x43\x01' becomes 'ABC\x01'). + #token_text = token_text.decode('unicode_escape', 'ignore') + + self.append(token_text) + + + def handle_equals(self, token_text): + if self.flags.var_line: + # just got an '=' in a var-line, different line breaking rules will apply + self.flags.var_line_tainted = True + + self.append(' ') + self.append(token_text) + self.append(' ') + + + def handle_operator(self, token_text): + space_before = True + space_after = True + + if self.flags.var_line and token_text == ',' and self.is_expression(self.flags.mode): + # do not break on comma, for ( var a = 1, b = 2 + self.flags.var_line_tainted = False + + if self.flags.var_line and token_text == ',': + if self.flags.var_line_tainted: + self.append(token_text) + self.flags.var_line_reindented = True + self.flags.var_line_tainted = False + self.append_newline() + return + else: + self.flags.var_line_tainted = False + + if self.last_text in ['return', 'throw']: + # return had a special handling in TK_WORD + self.append(' ') + self.append(token_text) + return + + if token_text == ':' and self.flags.in_case: + self.append(token_text) + self.append_newline() + self.flags.in_case = False + return + + if token_text == '::': + # no spaces around the exotic namespacing syntax operator + self.append(token_text) + return + + if token_text == ',': + if self.flags.var_line: + if self.flags.var_line_tainted: + # This never happens, as it's handled previously, right? + self.append(token_text) + self.append_newline() + self.flags.var_line_tainted = False + else: + self.append(token_text) + self.append(' ') + elif self.last_type == 'TK_END_BLOCK' and self.flags.mode != '(EXPRESSION)': + self.append(token_text) + if self.flags.mode == 'OBJECT' and self.last_text == '}': + self.append_newline() + else: + self.append(' ') + else: + if self.flags.mode == 'OBJECT': + self.append(token_text) + self.append_newline() + else: + # EXPR or DO_BLOCK + self.append(token_text) + self.append(' ') + # comma handled + return + elif token_text in ['--', '++', '!'] \ + or (token_text in ['+', '-'] \ + and self.last_type in ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) \ + or self.last_text in self.line_starters: + + space_before = False + space_after = False + + if self.last_text == ';' and self.is_expression(self.flags.mode): + # for (;; ++i) + # ^^ + space_before = True + + if self.last_type == 'TK_WORD' and self.last_text in self.line_starters: + space_before = True + + if self.flags.mode == 'BLOCK' and self.last_text in ['{', ';']: + # { foo: --i } + # foo(): --bar + self.append_newline() + + elif token_text == '.': + # decimal digits or object.property + space_before = False + + elif token_text == ':': + if self.flags.ternary_depth == 0: + self.flags.mode = 'OBJECT' + space_before = False + else: + self.flags.ternary_depth -= 1 + elif token_text == '?': + self.flags.ternary_depth += 1 + + if space_before: + self.append(' ') + + self.append(token_text) + + if space_after: + self.append(' ') + + + + def handle_block_comment(self, token_text): + + lines = token_text.replace('\x0d', '').split('\x0a') + # all lines start with an asterisk? that's a proper box comment + if not any(l for l in lines[1:] if (l.lstrip())[0] != '*'): + self.append_newline() + self.append(lines[0]) + for line in lines[1:]: + self.append_newline() + self.append(' ' + line.strip()) + else: + # simple block comment: leave intact + if len(lines) > 1: + # multiline comment starts on a new line + self.append_newline() + self.trim_output() + else: + # single line /* ... */ comment stays on the same line + self.append(' ') + for line in lines: + self.append(line) + self.append('\n') + self.append_newline() + + + def handle_inline_comment(self, token_text): + self.append(' ') + self.append(token_text) + if self.is_expression(self.flags.mode): + self.append(' ') + else: + self.append_newline_forced() + + + def handle_comment(self, token_text): + if self.wanted_newline: + self.append_newline() + else: + self.append(' ') + + self.append(token_text) + self.append_newline_forced() + + + def handle_unknown(self, token_text): + if self.last_text in ['return', 'throw']: + self.append(' ') + + self.append(token_text) + + + + + +def main(): + + argv = sys.argv[1:] + + try: + opts, args = getopt.getopt(argv, "s:c:o:djbkile:h", ['indent-size=','indent-char=','outfile=', 'disable-preserve-newlines', + 'jslint-happy', 'brace-style=', 'extra-expr-spacing=', + 'keep-array-indentation', 'indent-level=', 'help', + 'usage', 'stdin']) + except getopt.GetoptError: + usage() + sys.exit(2) + + js_options = default_options() + + file = None + outfile = 'stdout' + if len(args) == 1: + file = args[0] + for opt, arg in opts: + if opt in ('--keep-array-indentation', '-k'): + js_options.keep_array_indentation = True + elif opt in ('--outfile', '-o'): + outfile = arg + elif opt in ('--indent-size', '-s'): + js_options.indent_size = int(arg) + elif opt in ('--indent-char', '-c'): + js_options.indent_char = arg + elif opt in ('--disable-preserve_newlines', '-d'): + js_options.preserve_newlines = False + elif opt in ('--jslint-happy', '-j'): + js_options.jslint_happy = True + elif opt in ('--brace-style', '-b'): + js_options.brace_style = arg + elif opt in ('--extra-expr-spacing', '-e'): + try: + js_options.extra_expr_spacing = "".ljust(int(arg)) + except ValueError: + return usage() + elif opt in ('--stdin', '-i'): + file = '-' + elif opt in ('--help', '--usage', '--h'): + return usage() + + if file == None: + return usage() + else: + if outfile == 'stdout': + print(beautify_file(file, js_options)) + else: + f = open(outfile, 'w') + f.write(beautify_file(file, js_options) + '\n') + f.close() + + +if __name__ == "__main__": + main() + + diff --git a/tools/less-1.3.0.min.js b/tools/less-1.3.0.min.js new file mode 100644 index 00000000..309bf550 --- /dev/null +++ b/tools/less-1.3.0.min.js @@ -0,0 +1,9 @@ +// +// LESS - Leaner CSS v1.3.0 +// http://lesscss.org +// +// Copyright (c) 2009-2011, Alexis Sellier +// Licensed under the Apache 2.0 License. +// +(function(a,b){function c(b){return a.less[b.split("/")[1]]}function l(){var a=document.getElementsByTagName("style");for(var b=0;b0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&g&&(t("saving "+e+" to cache."),g.setItem(e,a),g.setItem(e+":timestamp",c))}function q(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var g=r(),h=f?!1:d.async;typeof g.overrideMimeType=="function"&&g.overrideMimeType("text/css"),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),f?g.status===0||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){g.readyState==4&&i(g,c,e)}:i(g,c,e)}function r(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){return t("browser doesn't support AJAX."),null}}function s(a){return a&&a.parentNode.removeChild(a)}function t(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function u(a,b){var c="less-error-message:"+o(b),e='
                                                                                                                                                                    1. {content}
                                                                                                                                                                    2. ',f=document.createElement("div"),g,h,i=[],j=a.filename||b;f.id=c,f.className="less-error-message",h="

                                                                                                                                                                      "+(a.message||"There is an error in your .less file")+"

                                                                                                                                                                      "+'

                                                                                                                                                                      in '+j+" ";var k=function(a,b,c){a.extract[b]&&i.push(e.replace(/\{line\}/,parseInt(a.line)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};a.stack?h+="
                                                                                                                                                                      "+a.stack.split("\n").slice(1).join("
                                                                                                                                                                      "):a.extract&&(k(a,0,""),k(a,1,"line"),k(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":

                                                                                                                                                                      "+"
                                                                                                                                                                        "+i.join("")+"
                                                                                                                                                                      "),f.innerHTML=h,p([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}typeof define=="function"&&define.amd&&define("less",[],function(){return d}),Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"||a instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=this.length>>>0;for(var d=0;d>>0,c=new Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c=b)return-1;c<0&&(c+=b);for(;cl&&(k[g]=k[g].slice(f-l),l=f)}function t(a){var c,d,e,h,i,j,n,o;if(a instanceof Function)return a.call(m.parsers);if(typeof a=="string")c=b.charAt(f)===a?a:null,e=1,s();else{s();if(c=a.exec(k[g]))e=c[0].length;else return null}if(c){o=f+=e,j=f+k[g].length-e;while(f=0&&b.charAt(c)!=="\n";c--)d++;return{line:typeof a=="number"?(b.slice(0,a).match(/\n/g)||"").length:null,column:d}}function A(a,b){var c=y(a,b),d=z(a.index,c),e=d.line,f=d.column,g=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.filename,this.index=a.index,this.line=typeof e=="number"?e+1:null,this.callLine=a.call&&z(a.call,c).line+1,this.callExtract=g[z(a.call,c).line],this.stack=a.stack,this.column=f,this.extract=[g[e-1],g[e],g[e+1]]}var b,f,g,h,i,j,k,l,m,n=this,o=function(){},p=this.imports={paths:a&&a.paths||[],queue:[],files:{},contents:{},mime:a&&a.mime,error:null,push:function(b,c){var e=this;this.queue.push(b),d.Parser.importer(b,this.paths,function(a,d,f){e.queue.splice(e.queue.indexOf(b),1),e.files[b]=d,e.contents[b]=f,a&&!e.error&&(e.error=a),c(a,d),e.queue.length===0&&o()},a)}};return this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null,m={imports:p,parse:function(h,i){var n,p,q,r,s,u,v=[],w,x=null;f=g=l=j=0,b=h.replace(/\r\n/g,"\n"),k=function(c){var d=0,e=/[^"'`\{\}\/\(\)\\]+/g,f=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,g=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`\\\r\n]|\\.)*)`/g,h=0,i,j=c[0],k;for(var l=0,m,n;l0&&(x=new A({index:l,type:"Parse",message:"missing closing `}`",filename:a.filename},a)),c.map(function(a){return a.join("")})}([[]]);if(x)return i(x);try{n=new e.Ruleset([],t(this.parsers.primary)),n.root=!0}catch(y){return i(new A(y,a))}n.toCSS=function(b){var f,g,h;return function(f,g){var h=[],i;f=f||{},typeof g=="object"&&!Array.isArray(g)&&(g=Object.keys(g).map(function(a){var b=g[a];return b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b])),new e.Rule("@"+a,b,!1,0)}),h=[new e.Ruleset(null,g)]);try{var j=b.call(this,{frames:h}).toCSS([],{compress:f.compress||!1})}catch(k){throw new A(k,a)}if(i=m.imports.error)throw i instanceof A?i:new A(i,a);return f.yuicompress&&d.mode==="node"?c("./cssmin").compressor.cssmin(j):f.compress?j.replace(/(\s)+/g,"$1"):j}}(n.eval);if(f=0&&b.charAt(z)!=="\n";z--)B++;x={type:"Parse",message:"Syntax Error on line "+s,index:f,filename:a.filename,line:s,column:B,extract:[u[s-2],u[s-1],u[s]]}}this.imports.queue.length>0?o=function(){i(x,n)}:i(x,n)},parsers:{primary:function(){var a,b=[];while((a=t(this.mixin.definition)||t(this.rule)||t(this.ruleset)||t(this.mixin.call)||t(this.comment)||t(this.directive))||t(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(b.charAt(f)!=="/")return;if(b.charAt(f+1)==="/")return new e.Comment(t(/^\/\/.*/),!0);if(a=t(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new e.Comment(a)},entities:{quoted:function(){var a,c=f,d;b.charAt(c)==="~"&&(c++,d=!0);if(b.charAt(c)!=='"'&&b.charAt(c)!=="'")return;d&&t("~");if(a=t(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new e.Quoted(a[0],a[1]||a[2],d)},keyword:function(){var a;if(a=t(/^[_A-Za-z-][_A-Za-z0-9-]*/))return e.colors.hasOwnProperty(a)?new e.Color(e.colors[a].slice(1)):new e.Keyword(a)},call:function(){var b,c,d=f;if(!(b=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(k[g])))return;b=b[1].toLowerCase();if(b==="url")return null;f+=b.length;if(b==="alpha")return t(this.alpha);t("("),c=t(this.entities.arguments);if(!t(")"))return;if(b)return new e.Call(b,c,d,a.filename)},arguments:function(){var a=[],b;while(b=t(this.entities.assignment)||t(this.expression)){a.push(b);if(!t(","))break}return a},literal:function(){return t(this.entities.dimension)||t(this.entities.color)||t(this.entities.quoted)},assignment:function(){var a,b;if((a=t(/^\w+(?=\s?=)/i))&&t("=")&&(b=t(this.entity)))return new e.Assignment(a,b)},url:function(){var a;if(b.charAt(f)!=="u"||!t(/^url\(/))return;return a=t(this.entities.quoted)||t(this.entities.variable)||t(this.entities.dataURI)||t(/^[-\w%@$\/.&=:;#+?~]+/)||"",u(")"),new e.URL(a.value||a.data||a instanceof e.Variable?a:new e.Anonymous(a),p.paths)},dataURI:function(){var a;if(t(/^data:/)){a={},a.mime=t(/^[^\/]+\/[^,;)]+/)||"",a.charset=t(/^;\s*charset=[^,;)]+/)||"",a.base64=t(/^;\s*base64/)||"",a.data=t(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var c,d=f;if(b.charAt(f)==="@"&&(c=t(/^@@?[\w-]+/)))return new e.Variable(c,d,a.filename)},color:function(){var a;if(b.charAt(f)==="#"&&(a=t(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new e.Color(a[1])},dimension:function(){var a,c=b.charCodeAt(f);if(c>57||c<45||c===47)return;if(a=t(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new e.Dimension(a[1],a[2])},javascript:function(){var a,c=f,d;b.charAt(c)==="~"&&(c++,d=!0);if(b.charAt(c)!=="`")return;d&&t("~");if(a=t(/^`([^`]*)`/))return new e.JavaScript(a[1],f,d)}},variable:function(){var a;if(b.charAt(f)==="@"&&(a=t(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!w(/^[@\w.%-]+\/[@\w.-]+/))return;if((a=t(this.entity))&&t("/")&&(b=t(this.entity)))return new e.Shorthand(a,b)},mixin:{call:function(){var c=[],d,g,h,i=f,j=b.charAt(f),k=!1;if(j!=="."&&j!=="#")return;while(d=t(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))c.push(new e.Element(g,d,f)),g=t(">");t("(")&&(h=t(this.entities.arguments))&&t(")"),t(this.important)&&(k=!0);if(c.length>0&&(t(";")||w("}")))return new e.mixin.Call(c,h||[],i,a.filename,k)},definition:function(){var a,c=[],d,g,h,i,j,k=!1;if(b.charAt(f)!=="."&&b.charAt(f)!=="#"||w(/^[^{]*(;|})/))return;q();if(d=t(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=d[1];do{if(b.charAt(f)==="."&&t(/^\.{3}/)){k=!0;break}if(!(h=t(this.entities.variable)||t(this.entities.literal)||t(this.entities.keyword)))break;if(h instanceof e.Variable)if(t(":"))i=u(this.expression,"expected expression"),c.push({name:h.name,value:i});else{if(t(/^\.{3}/)){c.push({name:h.name,variadic:!0}),k=!0;break}c.push({name:h.name})}else c.push({value:h})}while(t(","));u(")"),t(/^when/)&&(j=u(this.conditions,"expected condition")),g=t(this.block);if(g)return new e.mixin.Definition(a,c,g,j,k);r()}}},entity:function(){return t(this.entities.literal)||t(this.entities.variable)||t(this.entities.url)||t(this.entities.call)||t(this.entities.keyword)||t(this.entities.javascript)||t(this.comment)},end:function(){return t(";")||w("}")},alpha:function(){var a;if(!t(/^\(opacity=/i))return;if(a=t(/^\d+/)||t(this.entities.variable))return u(")"),new e.Alpha(a)},element:function(){var a,b,c,d;c=t(this.combinator),a=t(/^(?:\d+\.\d+|\d+)%/)||t(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||t("*")||t(this.attribute)||t(/^\([^)@]+\)/),a||t("(")&&(d=t(this.entities.variable))&&t(")")&&(a=new e.Paren(d));if(a)return new e.Element(c,a,f);if(c.value&&c.value.charAt(0)==="&")return new e.Element(c,null,f)},combinator:function(){var a,c=b.charAt(f);if(c===">"||c==="+"||c==="~"){f++;while(b.charAt(f)===" ")f++;return new e.Combinator(c)}if(c==="&"){a="&",f++,b.charAt(f)===" "&&(a="& ");while(b.charAt(f)===" ")f++;return new e.Combinator(a)}return b.charAt(f-1)===" "?new e.Combinator(" "):new e.Combinator(null)},selector:function(){var a,c,d=[],g,h;if(t("("))return a=t(this.entity),u(")"),new e.Selector([new e.Element("",a,f)]);while(c=t(this.element)){g=b.charAt(f),d.push(c);if(g==="{"||g==="}"||g===";"||g===",")break}if(d.length>0)return new e.Selector(d)},tag:function(){return t(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||t("*")},attribute:function(){var a="",b,c,d;if(!t("["))return;if(b=t(/^[a-zA-Z-]+/)||t(this.entities.quoted))(d=t(/^[|~*$^]?=/))&&(c=t(this.entities.quoted)||t(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!t("]"))return;if(a)return"["+a+"]"},block:function(){var a;if(t("{")&&(a=t(this.primary))&&t("}"))return a},ruleset:function(){var b=[],c,d,g;q();while(c=t(this.selector)){b.push(c),t(this.comment);if(!t(","))break;t(this.comment)}if(b.length>0&&(d=t(this.block)))return new e.Ruleset(b,d,a.strictImports);j=f,r()},rule:function(){var a,c,d=b.charAt(f),h,l;q();if(d==="."||d==="#"||d==="&")return;if(a=t(this.variable)||t(this.property)){a.charAt(0)!="@"&&(l=/^([^@+\/'"*`(;{}-]*);/.exec(k[g]))?(f+=l[0].length-1,c=new e.Anonymous(l[1])):a==="font"?c=t(this.font):c=t(this.value),h=t(this.important);if(c&&t(this.end))return new e.Rule(a,c,h,i);j=f,r()}},"import":function(){var a,b,c=f;if(t(/^@import\s+/)&&(a=t(this.entities.quoted)||t(this.entities.url))){b=t(this.mediaFeatures);if(t(";"))return new e.Import(a,p,b,c)}},mediaFeature:function(){var a,b,c=[];do if(a=t(this.entities.keyword))c.push(a);else if(t("(")){b=t(this.property),a=t(this.entity);if(!t(")"))return null;if(b&&a)c.push(new e.Paren(new e.Rule(b,a,null,f,!0)));else if(a)c.push(new e.Paren(a));else return null}while(a);if(c.length>0)return new e.Expression(c)},mediaFeatures:function(){var a,b=[];do if(a=t(this.mediaFeature)){b.push(a);if(!t(","))break}else if(a=t(this.entities.variable)){b.push(a);if(!t(","))break}while(a);return b.length>0?b:null},media:function(){var a,b;if(t(/^@media/)){a=t(this.mediaFeatures);if(b=t(this.block))return new e.Media(b,a)}},directive:function(){var a,c,d,g,h,i;if(b.charAt(f)!=="@")return;if(c=t(this["import"])||t(this.media))return c;if(a=t(/^@page|@keyframes/)||t(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)){g=(t(/^[^{]+/)||"").trim();if(d=t(this.block))return new e.Directive(a+" "+g,d)}else if(a=t(/^@[-a-z]+/))if(a==="@font-face"){if(d=t(this.block))return new e.Directive(a,d)}else if((c=t(this.entity))&&t(";"))return new e.Directive(a,c)},font:function(){var a=[],b=[],c,d,f,g;while(g=t(this.shorthand)||t(this.entity))b.push(g);a.push(new e.Expression(b));if(t(","))while(g=t(this.expression)){a.push(g);if(!t(","))break}return new e.Value(a)},value:function(){var a,b=[],c;while(a=t(this.expression)){b.push(a);if(!t(","))break}if(b.length>0)return new e.Value(b)},important:function(){if(b.charAt(f)==="!")return t(/^! *important/)},sub:function(){var a;if(t("(")&&(a=t(this.expression))&&t(")"))return a},multiplication:function(){var a,b,c,d;if(a=t(this.operand)){while(!w(/^\/\*/)&&(c=t("/")||t("*"))&&(b=t(this.operand)))d=new e.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,c,d,g;if(a=t(this.multiplication)){while((d=t(/^[-+]\s+/)||b.charAt(f-1)!=" "&&(t("+")||t("-")))&&(c=t(this.multiplication)))g=new e.Operation(d,[g||a,c]);return g||a}},conditions:function(){var a,b,c=f,d;if(a=t(this.condition)){while(t(",")&&(b=t(this.condition)))d=new e.Condition("or",d||a,b,c);return d||a}},condition:function(){var a,b,c,d,g=f,h=!1;t(/^not/)&&(h=!0),u("(");if(a=t(this.addition)||t(this.entities.keyword)||t(this.entities.quoted))return(d=t(/^(?:>=|=<|[<=>])/))?(b=t(this.addition)||t(this.entities.keyword)||t(this.entities.quoted))?c=new e.Condition(d,a,b,g,h):v("expected expression"):c=new e.Condition("=",a,new e.Keyword("true"),g,h),u(")"),t(/^and/)?new e.Condition("and",c,t(this.condition)):c},operand:function(){var a,c=b.charAt(f+1);b.charAt(f)==="-"&&(c==="@"||c==="(")&&(a=t("-"));var d=t(this.sub)||t(this.entities.dimension)||t(this.entities.color)||t(this.entities.variable)||t(this.entities.call);return a?new e.Operation("*",[new e.Dimension(-1),d]):d},expression:function(){var a,b,c=[],d;while(a=t(this.addition)||t(this.entity))c.push(a);if(c.length>0)return new e.Expression(c)},property:function(){var a;if(a=t(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}};if(d.mode==="browser"||d.mode==="rhino")d.Parser.importer=function(a,b,c,d){!/^([a-z]+:)?\//.test(a)&&b.length>0&&(a=b[0]+a),n({href:a,title:a,type:d.mime},function(e){e&&typeof d.errback=="function"?d.errback.call(null,a,b,c,d):c.apply(null,arguments)},!0)};(function(a){function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function d(a){return Math.min(1,Math.max(0,a))}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){return a=a<0?a+1:a>1?a-1:a,a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();return e.s+=c.value/100,e.s=d(e.s),b(e)},desaturate:function(a,c){var e=a.toHSL();return e.s-=c.value/100,e.s=d(e.s),b(e)},lighten:function(a,c){var e=a.toHSL();return e.l+=c.value/100,e.l=d(e.l),b(e)},darken:function(a,c){var e=a.toHSL();return e.l-=c.value/100,e.l=d(e.l),b(e)},fadein:function(a,c){var e=a.toHSL();return e.a+=c.value/100,e.a=d(e.a),b(e)},fadeout:function(a,c){var e=a.toHSL();return e.a-=c.value/100,e.a=d(e.a),b(e)},fade:function(a,c){var e=a.toHSL();return e.a=c.value/100,e.a=d(e.a),b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;return d.h=e<0?360+e:e,b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")}}}(c("../tree")),function(a){a.Comment=function(a,b){this.value=a,this.silent=!!b},a.Comment.prototype={toCSS:function(a){return a.compress?"":this.value},eval:function(){return this}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype.eval=function(a){var b=this.lvalue.eval(a),c=this.rvalue.eval(a),d=this.index,e,e=function(a){switch(a){case"and":return b&&c;case"or":return b||c;default:if(b.compare)e=b.compare(c);else if(c.compare)e=c.compare(b);else throw{type:"Type",message:"Unable to perform comparison",index:d};switch(e){case-1:return a==="<"||a==="=<";case 0:return a==="="||a===">="||a==="=<";case 1:return a===">"||a===">="}}}(this.op);return this.negate?!e:e}}(c("../tree")),function(a){a.Dimension=function(a,b){this.value=parseFloat(a),this.unit=b||null},a.Dimension.prototype={eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},toCSS:function(){var a=this.value+this.unit;return a},operate:function(b,c){return new a.Dimension(a.operate(b,this.value,c.value),this.unit||c.unit)},compare:function(b){return b instanceof a.Dimension?b.value>this.value?-1:b.value":a.compress?">":" > "}[this.value]}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS?b.toCSS(a):""}).join(" ")}}}(c("../tree")),function(a){a.Import=function(b,c,d,e){var f=this;this.index=e,this._path=b,this.features=d&&new a.Value(d),b instanceof a.Quoted?this.path=/\.(le?|c)ss(\?.*)?$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css(\?.*)?$/.test(this.path),this.css||c.push(this.path,function(b,c){b&&(b.index=e),f.root=c||new a.Ruleset([],[])})},a.Import.prototype={toCSS:function(a){var b=this.features?" "+this.features.toCSS(a):"";return this.css?"@import "+this._path.toCSS()+b+";\n":""},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.css)return this;c=new a.Ruleset([],this.root.rules.slice(0));for(var e=0;e1){var d=new a.Element("&",null,0),e=[new a.Selector([d])];c=new a.Ruleset(e,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(a.length===0)return[];if(a.length===1)return a[0];var b=[],c=this.permute(a.slice(1));for(var d=0;d0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;gthis.params.length)return!1;if(this.required>0&&c>this.params.length)return!1}if(this.condition&&!this.condition.eval({frames:[this.evalParams(b,a)].concat(b.frames)}))return!1;d=Math.min(c,this.arity);for(var f=0;fe.selectors[g].elements.length?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}}),this._lookups[g]=d)},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;this.root||(b.length===0?g=this.selectors.map(function(a){return[a]}):this.joinSelectors(g,b,this.selectors));for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":",\n"),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f),d.join("")+(c.compress?"\n":"")},joinSelectors:function(a,b,c){for(var d=0;d0&&e.push(new a.Selector(g)),h.length>0&&f.push(new a.Selector(h));for(var l=0;l0&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("../tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("../tree")),function(a){a.Variable=function(a,b,c){this.name=a,this.index=b,this.file=c},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{type:"Name",message:"variable "+e+" is undefined",filename:this.file,index:this.index}}}}(c("../tree")),function(a){a.find=function(a,b){for(var c=0,d;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)}}(c("./tree"));var f=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||f?"development":"production"),d.async=!1,d.poll=d.poll||(f?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&m(function(a,b,c,d,e){b&&p(b.toCSS(),d,e.lastModified)})},d.poll)):d.optimization=3;var g;try{g=typeof a.localStorage=="undefined"?null:a.localStorage}catch(h){g=null}var i=document.getElementsByTagName("link"),j=/^text\/(x-)?less$/;d.sheets=[];for(var k=0;k