Repository: tart/tartJS Branch: master Commit: 3b677e6969af Files: 144 Total size: 749.3 KB Directory structure: gitextract_2t11nxj1/ ├── .gitignore ├── .gitmodules ├── .travis.yml ├── README.md ├── gulpfile.js ├── package.json ├── scripts/ │ ├── build.sh │ ├── compileAllSpecRunners.sh │ ├── compileForSpecRunner.sh │ ├── deps.sh │ ├── jsdoc-conf.json │ └── runAllSpecRunners.sh ├── tart/ │ ├── Builder.js │ ├── Carousel/ │ │ ├── Carousel.js │ │ └── spec/ │ │ ├── CarouselSpec.js │ │ └── SpecRunner.html │ ├── CircularCarousel/ │ │ ├── CircularCarousel.js │ │ └── spec/ │ │ ├── CircularCarouselSpec.js │ │ └── SpecRunner.html │ ├── Collection.js │ ├── DropdownList/ │ │ ├── DropdownBuilder.js │ │ └── DropdownList.js │ ├── Err.js │ ├── FormValidator/ │ │ ├── FormValidator.js │ │ ├── README.md │ │ └── spec/ │ │ ├── FormValidatorSpec.js │ │ └── SpecRunner.html │ ├── List.js │ ├── Pagination/ │ │ ├── CircularPagination.js │ │ ├── Pagination.js │ │ └── spec/ │ │ ├── PaginationSpec.js │ │ └── SpecRunner.html │ ├── Registry.js │ ├── StateMachine/ │ │ ├── State.js │ │ └── StateMachine.js │ ├── Tabs/ │ │ ├── TabPanel.js │ │ └── Tabs.js │ ├── Validation/ │ │ ├── README.md │ │ ├── Validation.js │ │ └── spec/ │ │ ├── SpecRunner.html │ │ └── ValidationSpec.js │ ├── XhrManager.js │ ├── base/ │ │ ├── Model.js │ │ └── plugin/ │ │ ├── BasePlugin.js │ │ ├── Filter.js │ │ ├── Pager.js │ │ └── Sorter.js │ ├── components/ │ │ ├── Carousel/ │ │ │ ├── Controller.js │ │ │ ├── Model.js │ │ │ ├── Template.js │ │ │ ├── View.js │ │ │ └── Widget.js │ │ ├── Controller.js │ │ ├── RemoteModel.js │ │ ├── ThumbnailedCarousel/ │ │ │ ├── Model.js │ │ │ ├── SpotController.js │ │ │ ├── SpotTemplate.js │ │ │ ├── SpotView.js │ │ │ ├── ThumbnailsController.js │ │ │ ├── ThumbnailsTemplate.js │ │ │ ├── ThumbnailsView.js │ │ │ └── Widget.js │ │ ├── View.js │ │ ├── Widget.js │ │ └── spec/ │ │ ├── ComponentsSpec.js │ │ └── SpecRunner.html │ ├── dataProxy/ │ │ ├── Abstract.js │ │ ├── CircularLocal.js │ │ ├── Local.js │ │ └── Xhr.js │ ├── date/ │ │ ├── DateRange.js │ │ └── date.js │ ├── deps.js │ ├── dom/ │ │ └── dom.js │ ├── events/ │ │ ├── GestureHandler.js │ │ └── HoverHandler.js │ ├── events.js │ ├── externs/ │ │ ├── jasmine.externs.js │ │ ├── jquery-1.4.4.externs.js │ │ └── tart.externs.js │ ├── locale/ │ │ ├── en.js │ │ ├── locale.js │ │ └── tr.js │ ├── mock/ │ │ ├── index.html │ │ └── jQuery/ │ │ └── xhr.js │ ├── money/ │ │ ├── Currency.js │ │ ├── CurrencyTL.js │ │ ├── CurrencyUSD.js │ │ └── Money.js │ ├── mvc/ │ │ ├── Action.js │ │ ├── Application.js │ │ ├── Controller.js │ │ ├── IApplication.js │ │ ├── Layout.js │ │ ├── MobileAction.js │ │ ├── MobileRenderer.js │ │ ├── Model.js │ │ ├── README.md │ │ ├── Renderer.js │ │ ├── View.js │ │ ├── mvc.js │ │ ├── spec/ │ │ │ ├── SpecRunner.html │ │ │ └── mvcSpec.js │ │ ├── test/ │ │ │ ├── Bootstrapper.js │ │ │ ├── application/ │ │ │ │ ├── controllers/ │ │ │ │ │ ├── GamesController.js │ │ │ │ │ └── IndexController.js │ │ │ │ ├── mvcapp.js │ │ │ │ └── views/ │ │ │ │ ├── layouts/ │ │ │ │ │ ├── common.js │ │ │ │ │ └── rare.js │ │ │ │ └── scripts/ │ │ │ │ ├── games/ │ │ │ │ │ ├── index.js │ │ │ │ │ └── list.js │ │ │ │ └── index/ │ │ │ │ └── list.js │ │ │ └── index.html │ │ └── uri/ │ │ ├── Redirection.js │ │ ├── Request.js │ │ ├── Route.js │ │ └── Router.js │ ├── storage/ │ │ └── Storage.js │ ├── string/ │ │ └── string.js │ ├── tart.js │ └── ui/ │ ├── Component.js │ ├── ComponentManager.js │ ├── ComponentModel.js │ ├── DlgComponent.js │ ├── InfiniteScroll/ │ │ ├── InfiniteScrollComponent.js │ │ ├── InfiniteScrollComponentModel.js │ │ └── infinite-scroll.css │ ├── NavBar/ │ │ ├── NavBarComponent.js │ │ └── nav-bar.css │ ├── PullToRefresh/ │ │ ├── P2RComponent.js │ │ ├── P2RComponentModel.js │ │ └── pull-to-refresh.css │ ├── Sidebar/ │ │ ├── SidebarComponent.js │ │ └── sidebar.css │ ├── TabBar/ │ │ ├── TabBarView.js │ │ └── tabbar.css │ ├── View.js │ ├── ViewManager.js │ ├── ViewModel.js │ ├── input/ │ │ ├── DateComponent.js │ │ └── RevealingPassword.js │ └── tooltip/ │ ├── TooltipComponent.js │ └── TooltipComponentModel.js └── third_party/ └── jquery/ └── jquery-1.5.2.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.swp *.swo .idea *.DS_Store *.pyc compiled/* /atlassian-ide-plugin.xml compiled.js SpecRunnerCompiled.html node_modules docs ================================================ FILE: .gitmodules ================================================ [submodule "tools"] path = tools url = https://github.com/tart/GUI-Tools.git [submodule "third_party/jasmine"] path = third_party/jasmine url = https://github.com/pivotal/jasmine.git [submodule "third_party/goog"] path = third_party/goog url = https://github.com/tart/closure-library.git ================================================ FILE: .travis.yml ================================================ sudo: false language: node_js node_js: - '0.12' install: - npm -d install - npm install -g gulp before_script: - git config --global user.email "travis@travis-ci.org" - git config --global user.name "Travis-CI" script: - gulp travis env: global: - secure: DeieQoup1BuwAfLvz+2oRNQKEf2mrO65MsgMoBpWZirhRZvoYnzuWFVonByA6EyNPAzgzZI5Kk64aZaogbg9CzZu+mhkAuJE7m4+KgouE517wjbba8IseEFdyZ951I/VdaFp0PC33Xff32SB8L5xlzY+GnIzIUqTwa2rlogD7RA= ================================================ FILE: README.md ================================================ tartJS ====== tartJS is a performance-focused JavaScript framework based on well-tested Google Closure Tools suite. It provides rapid and responsive mobile app infrastructure. Check out [the documentation](http://tart.js.org/docs) and join us on [![tartJS Slack](http://slack.tartjs.org/badge.svg)](http://slack.tartjs.org) for anything about tartJS. Track issues on [![tartJS HuBoard](https://img.shields.io/github/issues/tart/tartjs.svg?style=flat&label=HuBoard)](https://huboard.com/tart/tartJS) Closure Compiler optimizes, checks and cleans up your annotated JavaScript code with best minification rates while helping you to catch errors. ## Getting Started ### From scratch Add tartJS to your project as a submodule: ```sh git submodule add git@github.com:tart/tartJS.git js/lib/tartJS ``` ### Boilerplates/Examples * For TodoMVC implementation and generic boilerplate: http://github.com/tart/tartjs-todomvc * For working mobile project: https://github.com/tart/tartjs-mobile-demo * Generic boilerplate: https://github.com/tart/BoilerPlate ## Documentation (More coming soon) ### Annotation With help of Closure Compiler, class based inheritance and type safety is widely used in tartJS. See [Annotating JavaScript for the Closure Compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler) how JSDoc annotation works. ### Directory structure * [tart](https://github.com/tart/tartJS/tree/master/tart) : Contains core Tart libraries * tools : Contains tools like Google Closure Compiler, Google Closure Linter etc (submodule to [tart/GUI-Tools](https://github.com/tart/GUI-Tools)) * [third_party](https://github.com/tart/tartJS/tree/master/third_party) : Contains third party libraries like Google Closure Library, Jasmine, jQuery etc. ## Examples * Outstanding web audio processor for guitar players: [Pedalboard.js](http://dashersw.github.io/pedalboard.js/) * Example TodoMVC application (useful as a web application boilerplate): [tartjs-todomvc](https://github.com/tart/tartjs-todomvc) * Example TV show mobile app (useful as a mobile boilerplate): [tartjs-mobile-demo](https://github.com/tart/tartjs-mobile-demo) ## Contributing Want to contribute? Great! Fork it, create a branch, make your changes, push and [open pull request](https://github.com/tart/tartJS/pulls) to contribute. Your pull request may not be merged immediately until necessary changes were made to match coding-style, pass tests. We do follow [Google JavaScript Style Guide](https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml). ## License Copyright 2014 Startup Kitchen. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: gulpfile.js ================================================ var gulp = require('gulp'); var shell = require("gulp-shell"); var runSequence = require('run-sequence'); var GH_TOKEN = process.env.GH_TOKEN || ''; gulp.task('jsdoc', shell.task([ 'rm -rf ./docs', 'jsdoc -c ./scripts/jsdoc-conf.json' ])); gulp.task('gh-pages', ['jsdoc'], shell.task([ 'mv docs docs_new', 'git remote add upstream https://' + GH_TOKEN + '@github.com/tart/tartJS.git/', 'git fetch upstream', 'git checkout -b gh-pages upstream/gh-pages', 'rm -rf docs', 'mv docs_new docs', 'git add docs', 'git commit -m "Update documentation"', 'git push upstream gh-pages', 'git checkout master' ])); gulp.task('travis-master', ['gh-pages']); gulp.task('travis-pull-request', []); gulp.task('travis', function(callback) { var task = process.env.TRAVIS_PULL_REQUEST != 'false' ? 'travis-pull-request' : 'travis-master'; runSequence(task, callback); }); ================================================ FILE: package.json ================================================ { "name": "tartJS", "description": "A performance-obsessed JavaScript framework for desktop, mobile and web applications.", "repository": { "type": "git", "url": "https://github.com/tart/tartJS" }, "dependencies": {}, "devDependencies": { "gulp": "^3.9.0", "gulp-shell": "^0.4.2", "jaguarjs-jsdoc": "git://github.com/davidshimjs/jaguarjs-jsdoc", "jsdoc": "^3.3.2", "run-sequence": "^1.1.2" } } ================================================ FILE: scripts/build.sh ================================================ #!/bin/bash #usage : scripts/build.sh ply.components.AnimatedCarouselExample.SpecRunner ply/components/AnimatedCarouselExample/spec/compiled.js namespace=$1 outputFile=$2 third_party/goog/closure/bin/build/closurebuilder.py \ --root="third_party/goog/" \ --root="tart/" \ --namespace="${namespace}" \ --compiler_flags="--externs=tart/externs/tart.externs.js" \ --compiler_flags="--externs=tart/externs/jasmine.externs.js" \ --compiler_flags="--externs=tart/externs/jquery-1.4.4.externs.js" \ --output_mode="compiled" --compiler_jar=tools/compiler/compiler.jar \ --output_file=${outputFile} \ --compiler_flags="--compilation_level=ADVANCED_OPTIMIZATIONS" \ --compiler_flags="--output_wrapper='(function(){%output%})()'" \ --compiler_flags="--create_source_map='compiled/source_map.js'" \ --compiler_flags="--property_renaming_report='compiled/properties.out'" \ --compiler_flags="--variable_renaming_report='compiled/variables.out'" \ --compiler_flags="--warning_level=VERBOSE" \ --compiler_flags="--formatting=PRETTY_PRINT" \ --compiler_flags="--formatting=PRINT_INPUT_DELIMITER" \ --compiler_flags="--jscomp_warning=accessControls" \ --compiler_flags="--jscomp_error=checkRegExp" \ --compiler_flags="--jscomp_error=checkTypes" \ --compiler_flags="--jscomp_error=nonStandardJsDocs" \ --compiler_flags="--jscomp_error=strictModuleDepCheck" ================================================ FILE: scripts/compileAllSpecRunners.sh ================================================ #!/bin/bash for i in `find * -type f | grep -i spec | grep -i "SpecRunner.html$" | grep -v "third_party"`; do compiledJs=`echo $i | sed -e 's/SpecRunner.html/compiled.js/'` echo -e "========= COMPILING $compiledJs =========" ; scripts/compileForSpecRunner.sh $compiledJs 2> /dev/null; echo -e "========= FINISHED $? =========\n"; done ================================================ FILE: scripts/compileForSpecRunner.sh ================================================ #!/bin/bash #usage : scripts/compileForSpecRunner.sh ply/components/AnimatedCarouselExample/spec/compiled.js outputFile=$1 if [[ $outputFile == *spec/compiled.js* ]] then namespace=`echo $outputFile | sed -e 's/\/spec\/compiled.js//g' | sed -e 's/\//\./g'` namespace=${namespace}.SpecRunner else echo "ERROR : Path should containt spec/compiled.js"; exit 1; fi third_party/goog/closure/bin/build/closurebuilder.py --root="third_party/goog/" --root="tart/" --root="ply/" --namespace="${namespace}" --compiler_flags="--externs=tart/externs/tart.externs.js" --compiler_flags="--externs=tart/externs/jasmine.externs.js" \ --compiler_flags="--externs=tart/externs/jquery-1.4.4.externs.js" \ --output_mode="compiled" --compiler_jar=tools/compiler/compiler.jar \ --output_file=${outputFile} \ --compiler_flags="--compilation_level=ADVANCED_OPTIMIZATIONS" \ --compiler_flags="--output_wrapper='(function(){%output%})()'" \ --compiler_flags="--create_source_map='compiled/source_map.js'" \ --compiler_flags="--property_renaming_report='compiled/properties.out'" \ --compiler_flags="--variable_renaming_report='compiled/variables.out'" \ --compiler_flags="--warning_level=VERBOSE" \ --compiler_flags="--formatting=PRETTY_PRINT" \ --compiler_flags="--formatting=PRINT_INPUT_DELIMITER" \ --compiler_flags="--jscomp_warning=accessControls" \ --compiler_flags="--jscomp_error=checkRegExp" \ --compiler_flags="--jscomp_error=checkTypes" \ --compiler_flags="--jscomp_error=nonStandardJsDocs" \ --compiler_flags="--jscomp_error=strictModuleDepCheck" ================================================ FILE: scripts/deps.sh ================================================ #!/bin/bash #usage : scripts/deps.sh third_party/goog/closure/bin/build/depswriter.py --root_with_prefix='tart/ ../../../../tart/' --output_file='tart/deps.js' ================================================ FILE: scripts/jsdoc-conf.json ================================================ { "tags": { "allowUnknownTags": true, "dictionaries": ["closure", "jsdoc"] }, "source": { "include": ["tart", "README.md"], "includePattern": ".+\\.js(doc)?$", "excludePattern": "(^|\\/|\\\\)_" }, "opts": { "template": "node_modules/jaguarjs-jsdoc", "encoding": "utf8", "destination": "docs/", "recurse": true }, "plugins": ["plugins/markdown"], "templates": { "cleverLinks": true, "monospaceLinks": false, "default": { "outputSourceFiles": true }, "applicationName": "TartJS", "disqus": "", "googleAnalytics": "", "openGraph": { "title": "", "type": "", "image": "", "site_name": "", "url": "" }, "meta": { "title": "tartJS", "description": "", "keyword": "" }, "linenums": true }, "markdown": { "tags": [] } } ================================================ FILE: scripts/runAllSpecRunners.sh ================================================ #!/bin/bash #run all SpecRunner*html files using qasmine for i in `find * -type f | grep -v third_party | grep -i SpecRunner | grep -i "\.html"`; do echo -e "========= RUNNUNG $i =========" ; qasmine $i 2> /dev/null; echo -e "========= FINISHED $? =========\n"; done ================================================ FILE: tart/Builder.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Generic builder class to build and modify DOM elements. */ goog.provide('tart.Builder'); goog.require('tart.Err'); /** * Constructor method * * @constructor * @param {string} id id for the builder. Can be used as the id of the DOM element this builder will build. * @return {tart.Builder} A builder object. */ tart.Builder = function(id) { this.id_ = id || ''; return this; }; /** * @type {jQueryObject} * @protected */ tart.Builder.prototype.$dom = null; /** * @type {Object} Templates holder object. */ tart.Builder.templates = {}; /** * Builds the DOM element. * @param {*} owner Owner class for the builder. */ tart.Builder.prototype.buildDOM = goog.abstractMethod; /** * Returns built ready-to-use DOM part in jQuery object format. * * @return {?jQueryObject} the DOM object built by the builder. */ tart.Builder.prototype.getDOM = function() { return this.$dom; }; /** * Removes the DOM part from document. */ tart.Builder.prototype.removeDOM = goog.abstractMethod; ================================================ FILE: tart/Carousel/Carousel.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.Carousel is an event driven Carousel/Image Slider class * which handles next and previous events and gets visible items on viewport. * * Example usage: * * var items = [ * {name : 'one'}, * {name : 'two'}, * {name : 'three'}, * {name : 'four'}, * {name : 'five'}, * {name : 'six'}, * {name : 'seven'} * ]; //seven items * * var carousel = new tart.Carousel(items); * * carousel.setItemPerViewport(2); //only 2 items is visibile * * goog.events.listen(carousel, tart.Carousel.EventTypes.NEXT, function (e) { * console.info('items moved next'); * console.log (e.itemsToBeRemoved); * console.log (e.itemsToBeInserted); * console.info(carousel.getVisibleItems()); * }); * * goog.events.listen(carousel, tart.Carousel.EventTypes.PREV, function (e) { * console.info('items moved prev'); * console.log (e.itemsToBeRemoved); * console.log (e.itemsToBeInserted); * console.info(carousel.getVisibleItems()); * }); * * //items : 'one', 'two' * carousel.next(1); * //items : 'two', 'three' * carousel.next(4); * //items : 'six', 'seven' * carousel.next(1); * //items : 'six', 'seven' which is end of items, for circular navigation use tart.CircularCarousel instead * carousel.prev(1); * //items : 'five', 'six' * carousel.prev(99999); * //items : 'one', 'two' */ goog.provide('tart.Carousel'); goog.provide('tart.Carousel.EventTypes'); goog.require('goog.debug.ErrorHandler'); goog.require('goog.events.EventHandler'); goog.require('goog.events.EventTarget'); /** * Pagination class to handle all paging events * * @param {Array.<*>=} items array of items. * @extends {goog.events.EventTarget} * @constructor */ tart.Carousel = function(items) { goog.events.EventTarget.call(this); /** @protected */ this.items = items; /** @protected */ this.itemCount = this.items.length; /** @protected */ this.itemPerViewport = 1; /** * First visible item index in viewport * * @protected * */ this.firstVisible = 0; /** * Last visible item index in viewport * * @protected * */ this.lastVisible = this.firstVisible + this.itemPerViewport; }; goog.inherits(tart.Carousel, goog.events.EventTarget); /** * Event types enumaration * * @enum {string} */ tart.Carousel.EventTypes = { MOVED: 'moved', PREV: 'prev', NEXT: 'next' }; /** * Item per visible viewport * * @param {number} itemPerViewport number of visible items. * @return {tart.Carousel} itself for chaining. */ tart.Carousel.prototype.setItemPerViewport = function(itemPerViewport) { this.itemPerViewport = itemPerViewport; this.lastVisible = this.firstVisible + itemPerViewport; return this; }; /** * Get number of visible items in viewport * * @return {number} number of visible items. */ tart.Carousel.prototype.getItemPerViewport = function() { return this.itemPerViewport; }; /** * Get visible items * * @return {Array.<*>} visible items array. */ tart.Carousel.prototype.getVisibleItems = function() { return this.items.slice(this.firstVisible, this.lastVisible); }; /** * Get visible items indexes * * @return {Object} visible items array. */ tart.Carousel.prototype.getVisibleItemIndexes = function() { var indexes = { first: this.firstVisible, last: this.lastVisible }; return indexes; }; /** * Calculate max move count * * @param {string} direction 'next' or 'prev' direction. * @param {number} moveCount number of movement. * @return {number} max move count. * @private */ tart.Carousel.prototype.getMaxMoveCount_ = function(direction, moveCount) { var maxMoveCount; if (direction == 'next') { maxMoveCount = this.itemCount - this.lastVisible; } else { maxMoveCount = this.firstVisible; } return maxMoveCount; }; /** * Find which items to be removed and inserted after move * * @param {number} moveCount item move count. * @return {Object} object literal which has itemsToBeInserted and itemsToBeRemoved nodes. */ tart.Carousel.prototype.getItemsToBeInsertedAndRemoved = function(moveCount) { var i, previousItemsIndex = [], nextItemsIndex = []; for (i = this.firstVisible; i < this.lastVisible; i++) { previousItemsIndex.push(i); nextItemsIndex.push(i + moveCount); } var moveDiff = this.getMoveDiff(previousItemsIndex, nextItemsIndex, moveCount); return moveDiff; }; /** * Get difference between visible items, after move and before move * * @param {Array.} a1 first array. * @param {Array.} a2 second array. * @param {number} moveCount item move count. * @return {Object} generated diff. * @protected */ tart.Carousel.prototype.getMoveDiff = function(a1, a2, moveCount) { var direction = (moveCount > 0) ? 'next' : 'prev'; moveCount = Math.abs(moveCount); var i = 0, index = 0, itemsToBeInserted = [], itemsToBeRemoved = [], itemCount = this.itemCount; var tmpItems; if (direction == 'prev') { tmpItems = { toBeInserted: a2.slice(0, moveCount), toBeRemoved: a1.slice(-1 * moveCount, a1.length) }; } else { tmpItems = { toBeRemoved: a1.slice(0, moveCount), toBeInserted: a2.slice(-1 * moveCount, a2.length) }; } for (i = 0; i < tmpItems.toBeInserted.length; i++) { index = (tmpItems.toBeInserted[i] + itemCount) % itemCount; itemsToBeInserted.push(this.items[index]); } for (i = 0; i < tmpItems.toBeRemoved.length; i++) { index = (tmpItems.toBeRemoved[i] + itemCount) % itemCount; itemsToBeRemoved.push(this.items[index]); } return { itemsToBeInserted: itemsToBeInserted, itemsToBeRemoved: itemsToBeRemoved }; }; /** * Move cursor to next or previous item * * @param {string} direction 'next' or 'prev' movement direction. * @param {*} moveCount cursor move count. * @protected */ tart.Carousel.prototype.move = function(direction, moveCount) { moveCount = moveCount || 1; moveCount = Math.abs(moveCount); var maxMoveCount = this.getMaxMoveCount_(direction, moveCount); moveCount = moveCount <= maxMoveCount ? moveCount : maxMoveCount; var eventToDispatch = tart.Carousel.EventTypes.NEXT; if (direction == 'prev') { moveCount = moveCount * -1; eventToDispatch = tart.Carousel.EventTypes.PREV; } var moveDiff = this.getItemsToBeInsertedAndRemoved(moveCount); this.firstVisible = this.firstVisible + moveCount; this.lastVisible = this.lastVisible + moveCount; var eventObj = {type: eventToDispatch, itemsToBeRemoved: moveDiff.itemsToBeRemoved, itemsToBeInserted: moveDiff.itemsToBeInserted}; this.dispatchEvent(eventObj); }; /** * Move cursor to next * * @param {number|*} moveCount cursor move count. */ tart.Carousel.prototype.next = function(moveCount) { this.move('next', moveCount); }; /** * Move cursor to previous * * @param {number|*} moveCount cursor move count. */ tart.Carousel.prototype.prev = function(moveCount) { this.move('prev', moveCount); }; ================================================ FILE: tart/Carousel/spec/CarouselSpec.js ================================================ goog.require('tart.Carousel'); goog.provide('tart.Carousel.SpecRunner'); describe('Carousel', function() { var carousel; var items = [ {name: 'one'}, {name: 'two'}, {name: 'three'}, {name: 'four'}, {name: 'five'}, {name: 'six'}, {name: 'seven'} ]; beforeEach(function() { carousel = new tart.Carousel(items); }); describe('some parameters should be set and get', function() { it('should set itemPerViewport', function() { carousel.setItemPerViewport(10); expect(carousel.getItemPerViewport()).toEqual(10); }); it('should return visible items', function() { carousel.setItemPerViewport(2); var visibleItems = carousel.getVisibleItems(); expect(visibleItems[0].name == 'one' && visibleItems[1].name == 'two').toBeTruthy(); }); }); describe('will navigate through items', function() { it('should move one item next', function() { carousel.setItemPerViewport(3); var previousItems = carousel.getVisibleItems(), visibleItems; goog.events.listen(carousel, tart.Carousel.EventTypes.NEXT, function(e) { visibleItems = carousel.getVisibleItems(); }); carousel.next(1); expect(visibleItems[0].name == 'two' && visibleItems[1].name == 'three').toBeTruthy(); }); it('should move more than item next', function() { carousel.setItemPerViewport(3); var visibleItems; goog.events.listen(carousel, tart.Carousel.EventTypes.NEXT, function(e) { visibleItems = carousel.getVisibleItems(); }); carousel.next(3); expect(visibleItems[0].name == 'four' && visibleItems[1].name == 'five').toBeTruthy(); }); it('should not move more than item count', function() { carousel.setItemPerViewport(2); var visibleItems; goog.events.listen(carousel, tart.Carousel.EventTypes.NEXT, function(e) { visibleItems = carousel.getVisibleItems(); }); carousel.next(99999); expect(visibleItems[0].name == 'six' && visibleItems[1].name == 'seven').toBeTruthy(); }); }); }); /** * Run jasmine spec */ tart.Carousel.SpecRunner = function() { jasmine.getEnv()['addReporter'](new jasmine.TrivialReporter()); jasmine.getEnv()['execute'](); }(); ================================================ FILE: tart/Carousel/spec/SpecRunner.html ================================================ Jasmine Test Runner ================================================ FILE: tart/CircularCarousel/CircularCarousel.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.CircularCarousel is an event driven Carousel/Image Slider class which * handles next and previous events and gets visible items on viewport. * * Example usage: * * var items = [ * {name : 'one'}, * {name : 'two'}, * {name : 'three'}, * {name : 'four'}, * {name : 'five'}, * {name : 'six'}, * {name : 'seven'} * ]; //seven items * * var carousel = new tart.CircularCarousel(items); * * carousel.setItemPerViewport(2); //only 2 items is visibile * * goog.events.listen(carousel, tart.Carousel.EventTypes.NEXT, function (e) { * console.info('items moved next'); * console.log (e.itemsToBeRemoved); * console.log (e.itemsToBeInserted); * console.info(carousel.getVisibleItems()); * }); * * goog.events.listen(carousel, tart.Carousel.EventTypes.PREV, function (e) { * console.info('items moved prev'); * console.log (e.itemsToBeRemoved); * console.log (e.itemsToBeInserted); * console.info(carousel.getVisibleItems()); * }); * * carousel.prev(3); * carousel.next(1); */ goog.provide('tart.CircularCarousel'); goog.require('goog.events.EventTarget'); goog.require('tart.Carousel'); /** * Pagination class to handle all paging events * * @param {Array.<*>=} items array of items. * @extends {tart.Carousel} * @constructor */ tart.CircularCarousel = function(items) { goog.base(this, items); }; goog.inherits(tart.CircularCarousel, tart.Carousel); /** * Find which items to be removed and inserted after move * * @param {number} moveCount item move count. * @return {Object} object literal which has itemsToBeInserted and itemsToBeRemoved nodes. */ tart.CircularCarousel.prototype.getItemsToBeInsertedAndRemoved = function(moveCount) { var i, previousItemsIndex = [], nextItemsIndex = [], start = this.firstVisible + moveCount; for (i = 0; i < this.lastVisible; i++) { previousItemsIndex.push(i); } for (i = start; i < start + this.itemPerViewport; i++) { nextItemsIndex.push(i); } var moveDiff = this.getMoveDiff(previousItemsIndex, nextItemsIndex, moveCount); return moveDiff; }; /** * low level move which handles next and prev methods * * @param {string} direction 'next' or 'prev' direction of movement. * @param {*} moveCount item move count. * @override * @protected */ tart.CircularCarousel.prototype.move = function(direction, moveCount) { moveCount = moveCount || 1; moveCount = Math.abs(moveCount); moveCount = moveCount % this.itemCount; var tmpCursor = 0; //default event dispatched var eventToDispatch = tart.Carousel.EventTypes.NEXT; if (direction == 'prev') { moveCount = moveCount * -1; tmpCursor = this.itemCount; eventToDispatch = tart.Carousel.EventTypes.PREV; } var tmp = [].concat(this.items).concat(this.items); var moveDiff = this.getItemsToBeInsertedAndRemoved(moveCount); this.firstVisible = this.firstVisible + tmpCursor + moveCount; this.lastVisible = this.lastVisible + tmpCursor + moveCount; this.items = tmp.slice(this.firstVisible, this.firstVisible + this.itemCount); this.firstVisible = 0; this.lastVisible = this.itemPerViewport; var eventObj = {type: eventToDispatch, itemsToBeRemoved: moveDiff.itemsToBeRemoved, itemsToBeInserted: moveDiff.itemsToBeInserted}; this.dispatchEvent(eventObj); }; ================================================ FILE: tart/CircularCarousel/spec/CircularCarouselSpec.js ================================================ goog.require('tart.CircularCarousel'); goog.provide('tart.CircularCarousel.SpecRunner'); describe('CircularCarousel', function() { var carousel; var items = [ {name: 'one'}, {name: 'two'}, {name: 'three'}, {name: 'four'}, {name: 'five'}, {name: 'six'}, {name: 'seven'} ]; beforeEach(function() { carousel = new tart.CircularCarousel(items); }); describe('extends from tart.Carousel', function() { it('is an isstance of tart.Carousel', function() { expect(carousel instanceof tart.Carousel).toBeTruthy(); }); }); describe('has circular navigation', function() { it('will navigate to seventh element after prev if current item is first item', function() { carousel.setItemPerViewport(2); var previousItems = carousel.getVisibleItems(), nextItems; goog.events.listen(carousel, tart.Carousel.EventTypes.PREV, function(e) { nextItems = carousel.getVisibleItems(); }); carousel.prev(1); expect(previousItems[0].name == 'one' && nextItems[0].name == 'seven').toBeTruthy(); }); it('will navigate to second element after 6 previous steps', function() { carousel.setItemPerViewport(2); var previousItems = carousel.getVisibleItems(), nextItems; goog.events.listen(carousel, tart.Carousel.EventTypes.PREV, function(e) { nextItems = carousel.getVisibleItems(); }); carousel.prev(6); expect(previousItems[0].name == 'one' && nextItems[0].name == 'two').toBeTruthy(); }); it('will navigate to seventh element after 8 previous steps which it means circular', function() { carousel.setItemPerViewport(2); var previousItems = carousel.getVisibleItems(), nextItems; goog.events.listen(carousel, tart.Carousel.EventTypes.PREV, function(e) { nextItems = carousel.getVisibleItems(); }); carousel.prev(8); expect(previousItems[0].name == 'one' && nextItems[0].name == 'seven').toBeTruthy(); }); it('will navigate to second element after 8 next steps which it means circular', function() { carousel.setItemPerViewport(2); var previousItems = carousel.getVisibleItems(), nextItems; goog.events.listen(carousel, tart.Carousel.EventTypes.NEXT, function(e) { nextItems = carousel.getVisibleItems(); }); carousel.next(8); expect(previousItems[0].name == 'one' && nextItems[0].name == 'two').toBeTruthy(); }); it('will navigate to third element after 2 next steps', function() { carousel.setItemPerViewport(2); var previousItems = carousel.getVisibleItems(), nextItems; goog.events.listen(carousel, tart.Carousel.EventTypes.NEXT, function(e) { nextItems = carousel.getVisibleItems(); }); carousel.next(2); expect(previousItems[0].name == 'one' && nextItems[0].name == 'three').toBeTruthy(); }); }); }); /** * Run jasmine spec */ tart.CircularCarousel.SpecRunner = function() { jasmine.getEnv()['addReporter'](new jasmine.TrivialReporter()); jasmine.getEnv()['execute'](); }(); ================================================ FILE: tart/CircularCarousel/spec/SpecRunner.html ================================================ Jasmine Test Runner ================================================ FILE: tart/Collection.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.Collection is a useful data construct that also features an "active item". It is a key-value pair * collection with easy to use methods. It is also event-driven, so observers are notified when the index of the * active item changes. * * Example usage: * * var items = { foo : 'bar' }; * var list = new tart.Collection(items); * * OR * * var items = ['A','B', 'C', 'D']; * var list = new tart.Collection(items); * * OR * * var items = { foo : 'bar', baz:'zoo' }; * var list = new tart.Collection(items, 1); // Set activeItemIndex to 1 */ goog.provide('tart.Collection'); goog.require('goog.array'); goog.require('goog.pubsub.PubSub'); goog.require('tart.Err'); /** * Constructor method * * @constructor * @extends {goog.pubsub.PubSub} * @param {tart.JSON=} initialList Default list items in JSON format. * @param {number=} activeItem Default selected item index. * @return {tart.Collection} A list object. */ tart.Collection = function(initialList, activeItem) { // define privileged methods and properties here... goog.base(this); var initials = initialList || []; this.activeItemIndex_ = (activeItem === undefined) ? -1 : activeItem; this.values_ = []; // Values of all list elements. this.keys_ = []; // Keys of all list elements. this.items_ = []; // All key-val pairs in collection. Format [{a:1},{b:2},{c:x}] // Initial values given like this: {a:b,c:d,e:f} //@todo This loop should be coded in generic javascript instead of jQuery. var _self = this; $.each(initials, function(a, b) { _self.addItem(a, b); }); this.setActiveItemIndex(this.activeItemIndex_); return this; }; goog.inherits(tart.Collection, goog.pubsub.PubSub); /** * Adds a new key-value pair to the collection. * * @param {number|string} key Key for the pair. * @param {*} value Value for the pair. * @return {boolean} Returns true if the add operation was successful. */ tart.Collection.prototype.addItem = function(key, value) { if (key === undefined || value === undefined) { throw new tart.Err('Missing arguments (key: ' + key + ' value: ' + value + ')- you must give a key' + 'and a value to add a new item.'); } else if (this.getByKey(key)) { // Deffensive check: key's must be unique throw new tart.Err('Key "' + key + '" already in collection. Keys should be unique.'); } else { var item = {}; item[key] = value; this.keys_.push(key); this.values_.push(value); this.items_.push(item); return true; } }; /** * Returns active item as JSON object. * * @return {tart.JSON} returns the active key-value pair from the collection. */ tart.Collection.prototype.getActiveItem = function() { return this.items_[this.getActiveItemIndex()]; }; /** * Returns active item's key. * * @return {string|number} returns the active key from the collection. */ tart.Collection.prototype.getActiveItemKey = function() { return this.keys_[this.getActiveItemIndex()]; }; /** * Returns active item's value. * * @return {*} returns the active value from the collection. */ tart.Collection.prototype.getActiveItemValue = function() { return this.values_[this.getActiveItemIndex()]; }; /** * Returns active item's index. * @return {number} returns the active index in the collection. */ tart.Collection.prototype.getActiveItemIndex = function() { return this.activeItemIndex_; }; /** * Returns item by given key. If not found, returns boolean false. * * @param {string|number} key key to search for. * @return {tart.JSON} returns the key-value pair given a specific key. */ tart.Collection.prototype.getByKey = function(key) { return this.items_[goog.array.indexOf(this.keys_, key)]; }; /** * Returns item by given value. If not found, returns boolean false. * * @param {string|number} val value to search for. * @return {tart.JSON} returns the key-value pair given a specific value. */ tart.Collection.prototype.getByValue = function(val) { return this.items_[goog.array.indexOf(this.values_, val)]; }; /** * Returns item by given index. * * @param {number} index Index to search in the collection. * @return {tart.JSON|boolean|undefined} returns the key-value pair given an index. */ tart.Collection.prototype.getByIndex = function(index) { if (index >= this.keys_.length || index < 0 || index === undefined || isNaN(index)) { return undefined; } else { return this.items_[index]; } }; /** * Dumps all items in an array. * @return {Array} returns all pairs as an array. */ tart.Collection.prototype.getAll = function() { return this.items_; }; /** Returns all values in an array. * @return {Array} returns all values in an array. */ tart.Collection.prototype.getValues = function() { return this.values_; }; /** Returns all keys in an array. * @return {Array} returns all keys in an array. */ tart.Collection.prototype.getKeys = function() { return this.keys_; }; /** * Returns all items in a kvp format. * @return {tart.JSON} obj returns an object that contains keys as its keys and values as its values. */ tart.Collection.prototype.getItems = function() { var obj = {}; for (var i = 0, l = this.items_.length; i < l; i++) { var item = this.items_[i]; $.each(item, function(key, value) { obj[key] = value; }); } return obj; }; /** * Removes an item from collection which given by index and rebuilds the item collection. * Returns new activeItemIndex if item successfully removed or FALSE if not. * * @param {number} index Index to remove. * @return {number|boolean|undefined} Condition of the operation or the active item index. */ tart.Collection.prototype.removeByIndex = function(index) { if (index >= this.keys_.length || index < 0 || index === undefined || isNaN(index)) { return undefined; } else { var oldActiveKey = this.getActiveItemKey(); this.items_.splice(index, 1); this.keys_.splice(index, 1); this.values_.splice(index, 1); var oldActiveIndex = goog.array.indexOf(this.keys_, oldActiveKey); if (oldActiveIndex > -1) { this.setActiveItemIndex(oldActiveIndex); } else { this.setActiveItemIndex(-1); } return this.activeItemIndex_; } }; /** * Removes items by key. * @param {string} key Game items key. */ tart.Collection.prototype.removeByKey = function(key) { var keys = this.getKeys(); var index = goog.array.indexOf(keys, key); this.removeByIndex(index); }; /** * Sets active item index. * @param {number} newIndex index to set as the new active item. * @return {boolean} Whether the method was able to set the item. */ tart.Collection.prototype.setActiveItemIndex = function(newIndex) { // Deffensive check; active item index should not greater than total items in collection. if (newIndex > this.values_.length - 1 || newIndex === undefined || isNaN(newIndex) || newIndex == this.activeItemIndex_) { return false; } else { var oldIndex = this.activeItemIndex_; this.activeItemIndex_ = newIndex; this.publish('indexChanged', this.activeItemIndex_, oldIndex); return true; } }; ================================================ FILE: tart/DropdownList/DropdownBuilder.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Builder class for dropdown lists. */ goog.require('tart.Builder'); goog.provide('tart.DropdownBuilder'); /** * DropdownBuilder class builds the DOM for DropdownList class. * * @constructor * @extends {tart.Builder} * @param {string} id id for the builder. Can be used as the id of the DOM element this builder will build. * @return {tart.DropdownBuilder} A builder object. */ tart.DropdownBuilder = function(id) { goog.base(this, id); this.owner = null; return this; }; goog.inherits(tart.DropdownBuilder, tart.Builder); /** * Main namespace of HTML templates for DOM structure. */ tart.DropdownBuilder.templates = {}; /** * Select menu template. * @param {string} id Id of the container. * @param {string} optionListHTML HTML of the list of options. * @return {string} the container HTML. */ tart.DropdownBuilder.templates.container = function(id, optionListHTML) { return ''; }; /** * Generates a select menu element, converts it to a jQuery object and passes to this.$dom. * * @param {tart.Collection} owner Owner tart.Collection instance. */ tart.DropdownBuilder.prototype.buildDOM = function(owner) { this.owner = owner; var opts = []; for (var k = 0, z = this.owner.keys_.length; k < z; k++) { var option = tart.DropdownBuilder.templates.option( this.owner.keys_[k], this.owner.values_[k], (k == this.owner.getActiveItemIndex())); opts.push(option); } var finalDOM = tart.DropdownBuilder.templates.container(this.id_, opts.join('')); this.$dom = $(finalDOM); this.$dom.change(function() { owner.switchIndex($(this).attr('selectedIndex')); }); }; /** * Generates a single option element which ready-to-use in a HTML select menu element. * * @param {string|number} key Option value. * @param {string|number} value Option value. * @param {boolean} selected Whether the option is selected. * @return {string} the option HTML. */ tart.DropdownBuilder.templates.option = function(key, value, selected) { var active = (selected) ? ' selected="selected"' : ''; return ''; }; /** * Sets an select option's "selected" property to True. * * @param {number} newIndex the new index of the element to set the dropdown list to. */ tart.DropdownBuilder.prototype.changeActiveItem = function(newIndex) { this.$dom.attr('selectedIndex', newIndex).change(); }; /** * Removes a single select option from DOM by index which given as parameter. * * @param {number} index the index of the element to remove from the list. */ tart.DropdownBuilder.prototype.removeOption = function(index) { this.$dom.children().eq(index).remove(); }; /** * Creates a single select option and attachs it to the current DOM. * * @param {string|number} key value for the option. * @param {string|number} value Text for the option. * @return {jQueryObject} the jQuery object that holds the DOM object of the dropdown list. */ tart.DropdownBuilder.prototype.addOption = function(key, value) { return this.$dom.append(tart.DropdownBuilder.templates.option(key, value, false)); }; /** * @inheritDoc */ tart.DropdownBuilder.prototype.removeDOM = function() { this.$dom.remove(); }; ================================================ FILE: tart/DropdownList/DropdownList.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Dropdown list class is an example of an HTML select box. * * Example usage: * * var items = {key1:'val1',key2:'val2',key3:'val3'}; * var builder = new tart.DropdownBuilder('elementId'); * var selectedItem = 1; * var list = new tart.DropdownList(items, builder, selectedItem); * list.getAll(); // Outputs [{key1:'val1'},{key2:'val2'},{key3:'val3'}] * list.getDOM(); // Returns a select (dropdown) menu in jquery format jQuery(select#elementId) * list.setActiveItemIndex(2); // Sets key3:val3 option element selected property to TRUE; * list.getActiveItemIndex(); // Returns an Object { key3="val3" } */ goog.provide('tart.DropdownList'); goog.require('tart.Collection'); goog.require('tart.DropdownBuilder'); /** * Constructor method for DropdownList. * * @constructor * @extends {tart.Collection} * @param {Object|Array} initialList initial list of items. * @param {tart.Builder=} opt_builder builder class. * @param {number=} opt_activeItem index of the active item. */ tart.DropdownList = function(initialList, opt_builder, opt_activeItem) { goog.base(this, initialList, opt_activeItem); this.builder = opt_builder || new tart.DropdownBuilder(''); this.builder.buildDOM(this); }; goog.inherits(tart.DropdownList, tart.Collection); /** * @inheritDoc */ tart.DropdownList.prototype.removeByIndex = function(index) { var result = this.constructor.superClass_.removeByIndex.call(this, index); if (result === false) { return -1; } else if (result === -1) { this.setActiveItemIndex(0); return 0; } else { if (this.builder) { this.builder.removeOption(result); } return result; } }; /** * Removes current DOM element of dropdownlist instance from window.document. */ tart.DropdownList.prototype.removeDOM = function() { this.builder.removeDOM(); }; /** * Returns current DOM element of dropdownlist instance. * * @return {jQueryObject} The jQuery object that wraps the DOM. */ tart.DropdownList.prototype.getDOM = function() { return this.builder.getDOM(); }; /** * @inheritDoc */ tart.DropdownList.prototype.setActiveItemIndex = function(newIndex) { this.switchIndex(newIndex); if (this.builder) { this.builder.changeActiveItem(this.getActiveItemIndex()); return true; } return false; }; /** * Set active item belongs to value parameter. * @param {(string|number)} value of an array. */ tart.DropdownList.prototype.setActiveItemByValue = function(value) { var that = this; var items = this.getValues(); var index = goog.array.findIndex(items, function(item) { return value == item; }); that.setActiveItemIndex(index); }; /** * Triggered by this.builder.dom_ element when user change the index by non-programatically way. * Important: This method shouldn't be called in any implementation code. Developers should use * this.setActiveItemIndex() method instead of this. * * @param {number} newIndex index of the item to set as active. */ tart.DropdownList.prototype.switchIndex = function(newIndex) { this.constructor.superClass_.setActiveItemIndex.call(this, ((newIndex < 0) ? 0 : newIndex)); }; /** * @inheritDoc */ tart.DropdownList.prototype.addItem = function(key, value) { var added = this.constructor.superClass_.addItem.call(this, key, value); if (this.builder && added === true) { this.builder.addOption(key, value); return true; } return false; }; ================================================ FILE: tart/Err.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tartJS error library and utility functions. */ goog.provide('tart.Err'); goog.require('tart'); /** * Returns a new Error object which contains a custom error name and message. * * @param {string=} message Message to set as the error message. * @param {string=} name Optional error name. * @extends {Error} * @constructor */ tart.Err = function(message, name) { this.name = name || 'Error'; this.message = message || ''; }; goog.inherits(tart.Err, Error); /** * Returns a new Error object which contains a implementation error message. * * @param {string} methodName Name of the unimplemented method. This will be set as the error message. * @throws {Error} Error object containing the details. */ tart.Err.unimplementedMethod = function(methodName) { throw new tart.Err('Wrong implementation', 'You should implement your own ' + methodName + ' method in child class which you want to use.'); }; ================================================ FILE: tart/FormValidator/FormValidator.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.FormValidator is a form validation library which uses tart.Validation instance as validator. * * Example usage: * * var form = $("form"); * var validationForSubmit = function (errors) { * //do some stuff with 'errors' object * }; * var validationForBlur = function (errors) { * //do some stuff with 'errors' object * }; * tart.FormValidator(form).validateOnSubmit(validationForSubmit); * tart.FormValidator(form).validateOnBlur(validateOnBlur); * * More examples can be seen from spec/FormValidationSpec.js file */ goog.require('tart.Validation'); goog.provide('tart.FormValidator'); /** * Attach validator to formEl * * @param {Object} formEl jQuery element for selected form. * @constructor * @return {tart.FormValidator} . */ tart.FormValidator = function(formEl) { //TODO: this is tightly coupled to tart.Validation /* @protected */ this.validator = tart.Validation; /* @protected */ this.form = formEl; /* @protected */ this.errors = []; return this; }; /** * Set validation rules to attached form * * @param {Object} rules given rules in object literal notation. * @return {tart.FormValidator} . * @this {tart.FormValidator} . */ tart.FormValidator.prototype.setRules = function(rules) { this.rules = rules; return this; }; /** * Find element with elementName in given form object * * @param {string} elementName name of element which to be find in form. * @return {Object} jQuery object for given element. */ tart.FormValidator.prototype.getFormElementByName = function(elementName) { var el = this.form.find('input[name=' + elementName + ']'); return el; }; /** * Find related element attribute for given input type * * @param {Object} el jQuery object of element. * @return {string} elements value for related input type. */ tart.FormValidator.prototype.getElementAttributeToCheck = function(el) { //TODO: this can vary on elements type such has "attr" return el.val(); }; /** * Rule key for tart.Validation * * @param {string} ruleKey key of rule. * @return {Function} elements value for related input type. */ tart.FormValidator.prototype.getValidationRuleByKey = function(ruleKey) { var rule; //TODO: this swich case should be looked up from an object literal switch (ruleKey) { case 'isEmail' : rule = this.validator.is.email; break; case 'isNotOnlySpace' : rule = this.validator.is.notOnlySpace; break; case 'isNumeric' : rule = this.validator.is.numeric; break; case 'isDigitAndNonDigit' : rule = this.validator.is.digitAndNonDigit; break; case 'hasMaxLength' : rule = this.validator.has.maxLength; break; case 'hasMinLength' : rule = this.validator.has.minLength; break; case 'hasMaxValue' : rule = this.validator.has.maxValue; break; case 'hasMinValue' : rule = this.validator.has.minValue; break; } return rule; }; /** * Get rule key and rule options from rule object * * @param {Object} rule rule object whom key is ruleName and value is rule options. * @return {Object} object which has .key and .options nodes. */ tart.FormValidator.prototype.getRuleKeyAndOptions = function(rule) { var results = []; //TODO: there should be a smarter way to do this for (var i in rule) { results.push({key: i, options: rule[i]}); } return results; }; /** * Apply rule and generate result in object literal * * @param {Object} el jQuery object to rule rule object whom key is ruleName and value is rule options. * @param {Object} rule rule to be applied to el. * @return {Object} result object which has .success and .item nodes. */ tart.FormValidator.prototype.applyRule = function(el, rule) { var value = this.getElementAttributeToCheck(el); var keyAndOptionsArray = this.getRuleKeyAndOptions(rule); var keyAndOptions, key, validationRule, options, result, failed = false; for (var i = 0; i < keyAndOptionsArray.length; i++) { keyAndOptions = keyAndOptionsArray[i]; key = keyAndOptions.key; options = keyAndOptions.options; validationRule = this.getValidationRuleByKey(key); result = validationRule(value, options.value); //return on first error if (!result) { break; } } return {success: result, item: {el: el, text: options.text}}; }; /** * Validate given form object with given rules, if any errors occured this.errors array will be populated * * @return {tart.FormValidator} . */ tart.FormValidator.prototype.validate = function() { this.errors = []; var el, rule, result; for (var i in this.rules) { el = this.getFormElementByName(i); rule = this.rules[i]; result = this.applyRule(el, rule); if (!result.success) { this.errors.push(result.item); break; } } return this; }; /** * Check if validation operation is successful or not by looking at this.errors array * * @return {boolean} validation is successful or not. */ tart.FormValidator.prototype.isValid = function() { if (this.errors.length == 0) { return true; } else { return false; } }; /** * Get generated errors array which contains element (el) and error text(text) * * @return {Array} errors array. */ tart.FormValidator.prototype.getErrors = function() { return this.errors; }; /** * Validate form on submit * * @param {Function} callback callback function after submit. */ tart.FormValidator.prototype.validateOnSubmit = function(callback) { callback = callback || function() {}; var that = this; this.form.submit(function(e) { that.validate(); if (!that.isValid()) { //if an error occured e.preventDefault(); e.stopImmediatePropagation(); //stop other events propagation callback(that.getErrors()); } }); }; ================================================ FILE: tart/FormValidator/README.md ================================================ # Tart FormValidator A JavaScript validation library which validates form elements using tart.Validation ## Testing specs You need [jasmine](http://pivotal.github.com/jasmine/) to run the specs You better have [qasmine](https://github.com/tart/qasmine) to automatize the testing process ### How to run qasmine Simlpy, run qasmine spec/SpecRunner.html ## Validation checks * Rules should be written in object literal like this
var rules = {
    testInput1 : {
        isNumeric : {
            text : "Input is not numeric"
        },
        hasMaxLength : {
            text : "Input's length is more than 9",
            value : 9
        },
        hasMinLength : {
            text : "Input's length is less than 6",
            value : 6
        }
    }
};

var form = $("form");

var rules = {
    testInput1 : {
        isNumeric : {
            text : "Input is not numeric"
        },
        hasMaxLength : {
            text : "Input's length is more than 9",
            value : 9
        },
        hasMinLength : {
            text : "Input's length is less than 6",
            value : 6
        }
    }
};


var validator = new tart.FormValidator(form);

validator.setRules(rules).validate();

if(validator.isValid()) {
    //its valid
}
else {
    //its not valid, do some stuff with error object
    var errors = validator.getErrors();
}
### Form bindings It can also binded before form submit and validation process can be done automatically and results will be returned to a callback function ================================================ FILE: tart/FormValidator/spec/FormValidatorSpec.js ================================================ goog.require('tart.FormValidator'); goog.provide('tart.FormValidator.SpecRunner'); describe('Form Validator', function() { var validator, form, formFields = []; beforeEach(function() { //create mock form object form = $('
'); formFields[0] = $('').attr('name', 'testInput1').appendTo(form); formFields[1] = $('').attr('name', 'testInput2').appendTo(form); validator = new tart.FormValidator(form); }); it('validator should be object', function() { expect(typeof validator).toEqual('object'); }); describe('Form validator for email', function() { var ruleErrorText = 'Not a valid email'; var rules = { 'testInput1': { 'isEmail': { 'text': ruleErrorText } } }; it("should validate an input which has 'foo@bar.com' as valid email", function() { formFields[0].val('foo@bar.com'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should not validate in input which has 'foo@bar.xxxxx' as valid email", function() { formFields[0].val('foo@bar.xxxxx'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should not validate in input which has 'foo@bar.xxxxx' with text 'Not a valid email'", function() { formFields[0].val('foo@bar.xxxxx'); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual(ruleErrorText); }); it("should not validate in input which has '' an errr text should be 'Not a valid email'", function() { formFields[0].val(' '); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual(ruleErrorText); }); }); describe('Form validator for not only space', function() { var ruleErrorText = 'You have ve to write at least one char or digit'; var rules = { 'testInput1': { 'isNotOnlySpace': { 'text': ruleErrorText } } }; it("should validate an input which starts with space but has some chars like ' foo bar '", function() { formFields[0].val(' foo bar '); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should not validate an input which has only spaces like ' '", function() { formFields[0].val(' '); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should not validate an empty string ''", function() { formFields[0].val(''); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should return error text 'You have ve to write at least one char or digit' on error", function() { formFields[0].val(''); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual(ruleErrorText); }); }); describe('Form validator for numeric control', function() { var ruleErrorText = 'You can type only numbers between 0-9'; var rules = { 'testInput1': { 'isNumeric': { 'text': ruleErrorText } } }; it("should validate an input which has only numbers like '12345'", function() { formFields[0].val('12345'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should not validate an input which has numbers and space like ' 1234 5'", function() { formFields[0].val(' 1234 5'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should not validate an input which has numbers and chars like '12345foo'", function() { formFields[0].val('12345foo'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should not validate input with text 'You can type only numbers between 0-9' for 'foo12345bar'", function() { formFields[0].val('foo12345foo'); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual(ruleErrorText); }); }); describe('Form validator for input which contains both digit and non digit char', function() { var ruleErrorText = 'Your input must contain both digit and non digit char'; var rules = { 'testInput1': { 'isDigitAndNonDigit': { 'text': ruleErrorText } } }; it("should not validate an input which has only numbers like '12345'", function() { formFields[0].val('12345'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should not validate an input which has only alpha chars 'foobar'", function() { formFields[0].val('foobar'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should not validate an input which has only spaces ' '", function() { formFields[0].val(' '); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should validate an input which has both digit and alpha chars 'foo12345bar'", function() { formFields[0].val('foo12345foo'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should validate an input which has both digit, alpha chars,no nalpha chars 'foo12345bar-_`'", function() { formFields[0].val('foo12345foo-_`'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should validate an input which has both digit and space chars like '12345 '", function() { formFields[0].val('12345 '); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should not validate an input which has both alpha and space chars like 'foobar '", function() { formFields[0].val('foobar '); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should not validate an input which has non alpha chars and space chars like '~-? '", function() { formFields[0].val('~-? '); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should return text 'Your input must contain both digit and non digit char' for invalid input", function() { formFields[0].val('~-? '); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual(ruleErrorText); }); }); describe('Form validator to check inputs max length', function() { var ruleErrorText = 'Your inputs lenght is more than 7'; var rules = { 'testInput1': { 'hasMaxLength': { 'text': ruleErrorText, 'value': 7 } } }; it("should validate an input like '12345'", function() { formFields[0].val('12345'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should not validate an input like '12345 '", function() { formFields[0].val('12345 '); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should not validate an input like ' '", function() { formFields[0].val(' '); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should validate an input like '123 5 7'", function() { formFields[0].val('123 5 7'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should return 'Your inputs lenght is more than 7' text on error", function() { formFields[0].val('123 5 7 foo bar'); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual(ruleErrorText); }); }); describe('Form validator to check inputs min length', function() { var ruleErrorText = 'Your inputs lenght is less than 3'; var rules = { 'testInput1': { 'hasMinLength': { 'text': ruleErrorText, 'value': 3 } } }; it("should validate an input like '12345'", function() { formFields[0].val('12345'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should not validate an input like '12'", function() { formFields[0].val('12'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should not validate an input like ' '", function() { formFields[0].val(' '); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should validate an input like '1 '", function() { formFields[0].val('1 '); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should return 'Your inputs lenght is less than 3' text on error", function() { formFields[0].val('12'); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual(ruleErrorText); }); }); describe("Form validator to check input's numeric value", function() { var ruleErrorText = 'Your inputs value is more than 10'; var rules = { 'testInput1': { 'hasMaxValue': { 'text': ruleErrorText, 'value': 10 } } }; it("should validate an input like '1'", function() { formFields[0].val('1'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should not validate an input like '12'", function() { formFields[0].val('12'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should return 'Your inputs value is more than 10' text on error", function() { formFields[0].val('12'); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual(ruleErrorText); }); }); describe("Form validator to check input's numeric value", function() { var ruleErrorText = 'Your inputs value is less than 10'; var rules = { 'testInput1': { 'hasMinValue': { 'text': ruleErrorText, 'value': 10 } } }; it("should not validate an input like '1'", function() { formFields[0].val('1'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should validate an input like '12'", function() { formFields[0].val('12'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should return 'Your inputs value is less than 10' text on error", function() { formFields[0].val('1'); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual(ruleErrorText); }); }); describe('Form validator for multiple rules', function() { var rules = { 'testInput1': { 'isNumeric': { 'text': 'Input is not numeric' }, 'hasMaxLength': { 'text': "Input's length is more than 9", 'value': 9 }, 'hasMinLength': { 'text': "Input's length is less than 6", 'value': 6 } } }; it("should not validate an input like 'fooobar'", function() { formFields[0].val('fooobar'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should validate an input like '123456'", function() { formFields[0].val('123456'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeTruthy(); }); it("should not validate an input like '12345'", function() { formFields[0].val('12345'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("should not validate an input like '12345678910'", function() { formFields[0].val('12345678910'); validator.setRules(rules).validate(); expect(validator.isValid()).toBeFalsy(); }); it("error text should be 'Input is not numeric' for 'foobar'", function() { formFields[0].val('foobar'); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual('Input is not numeric'); }); it("error text should be 'Input\'s length is more than 9' for '1234567890'", function() { formFields[0].val('1234567890'); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual("Input's length is more than 9"); }); it("error text should be 'Input\'s length is less than 6' for '12345'", function() { formFields[0].val('12345'); validator.setRules(rules).validate(); var errorText = validator.getErrors()[0].text; expect(errorText).toEqual("Input's length is less than 6"); }); }); describe("Form validate validates form before form's submit", function() { var rules = { 'testInput1': { 'isNumeric': { 'text': 'Input is not numeric' }, 'hasMaxLength': { 'text': "Input's length is more than 9", 'value': 9 }, 'hasMinLength': { 'text': "Input's length is less than 6", 'value': 6 } } }; it('should validate form on submit and return generated errors in callback', function() { formFields[0].val('foobar'); var errorObjects; validator.setRules(rules).validateOnSubmit(function(errors) { errorObjects = errors; }); form.trigger('submit'); expect(errorObjects[0].el.get(0)).toEqual(formFields[0].get(0)); expect(errorObjects[0].text).toEqual('Input is not numeric'); }); }); }); /** * Run jasmine spec */ tart.FormValidator.SpecRunner = function() { jasmine.getEnv()['addReporter'](new jasmine.TrivialReporter()); jasmine.getEnv()['execute'](); }(); ================================================ FILE: tart/FormValidator/spec/SpecRunner.html ================================================ Jasmine Test Runner ================================================ FILE: tart/List.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.List to handle List typed data structures extended from tart.Collection. */ goog.provide('tart.List'); goog.require('tart.Collection'); /** * List data structure * * @extends {tart.Collection} * @constructor */ tart.List = function() { goog.base(this); /** @private **/ this.keyCount_ = 0; }; goog.inherits(tart.List, tart.Collection); /** * Adds a new item to list . * * @param {*} value Value for the pair. * @return {boolean} . */ tart.List.prototype.addItem = function(value) { return tart.Collection.prototype.addItem.call(this, this.keyCount_++, value); }; ================================================ FILE: tart/Pagination/CircularPagination.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. goog.require('tart.Pagination'); goog.provide('tart.CircularPagination'); /** * CircularPagination class to handle circular paging events * * @constructor * @extends {tart.Pagination} */ tart.CircularPagination = function() { goog.base(this); }; goog.inherits(tart.CircularPagination, tart.Pagination); /** * Overriding tart.Pagination's hasPrev method, to return true for all conditions to make circular pages. * * @override * @return {boolean} Whether previous page is available . */ tart.CircularPagination.prototype.hasPrev = function() { return true; }; /** * Overriding tart.Pagination's hasNext method, to return true for all conditions to make circular pages. * * @override * @return {boolean} Whether next page is available . */ tart.CircularPagination.prototype.hasNext = function() { return true; }; /** * @override */ tart.CircularPagination.prototype.getNext = function() { return this.currentPage == this.getTotalPage() ? 1 : this.currentPage + 1; }; /** * @override */ tart.CircularPagination.prototype.getPrev = function() { return this.currentPage == 1 ? this.getTotalPage() : this.currentPage - 1; }; /** * @override */ tart.CircularPagination.prototype.setCurrentPage = function(page) { if (this.getTotalItems() > this.getItemPerPage()) { var oldValue = this.currentPage; this.currentPage = page; this.triggerPageChange_(oldValue, page); } return this; }; ================================================ FILE: tart/Pagination/Pagination.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.Pagination is an event driven Pagination object. * * Example usage: * * var paginator = new tart.Pagination(); * * paginator.setTotalPage(5); * paginator.setCurrentPage(2); * * goog.events.listen(paginator, tart.Pagination.EventTypes.PAGE_CHANGED, function (e) { * console.log("page changed"); * console.log("old page : " + e.oldValue); * console.log("new page : " + e.newValue); * }); * * paginator.setCurrentPage(4); // oldPage : 2, newPage : 4 * paginator.setCurrentPage(7); // oldPage : 4, newpage : 5 (totalPage) * paginator.prev(); // oldPage : 5, newPage: 4 * paginator.setCurrentPage(0); // oldPage : 4, newpage : 1 (at least 1) * paginator.next(); // oldPage : 1, newPage : 2 * */ goog.require('goog.debug.ErrorHandler'); goog.require('goog.events.EventHandler'); goog.require('goog.events.EventTarget'); goog.provide('tart.Pagination'); /** * Pagination class to handle all paging events * * @constructor * @extends {goog.events.EventTarget} */ tart.Pagination = function() { goog.events.EventTarget.call(this); /** @private */ this.totalPage_ = 1; /** @protected */ this.currentPage = 1; /** @private */ this.itemPerPage_ = 1; /** @private */ this.totalItems_ = 1; }; goog.inherits(tart.Pagination, goog.events.EventTarget); /** * Event types enumaration * * @enum {string} */ tart.Pagination.EventTypes = { PAGE_CHANGED: 'pageChanged' }; /** * Trigger page change event * * @param {string|number} oldValue old page value. * @param {string|number} newValue new page value. * @protected */ tart.Pagination.prototype.triggerPageChange_ = function(oldValue, newValue) { if (oldValue != newValue) this.dispatchEvent({type: tart.Pagination.EventTypes.PAGE_CHANGED, oldValue: oldValue, newValue: newValue}); }; /** * Get total page count * * @return {number} page count. */ tart.Pagination.prototype.getTotalPage = function() { return this.totalPage_; }; /** * Set total page count * * @param {number} page page count. * @return {tart.Pagination} . */ tart.Pagination.prototype.setTotalPage = function(page) { this.totalPage_ = page; this.totalItems_ = page * this.itemPerPage_; return this; }; /** * Get current page * * @return {number} current page. */ tart.Pagination.prototype.getCurrentPage = function() { return this.currentPage; }; /** * Set current page * * @param {number} page current page. * @return {tart.Pagination} . */ tart.Pagination.prototype.setCurrentPage = function(page) { var oldValue = this.currentPage; //if page > totalPage page = (page > this.totalPage_) ? this.totalPage_ : page; //if page < 1 page = (page < 1) ? 1 : page; this.currentPage = page; this.triggerPageChange_(oldValue, page); return this; }; /** * Get total item count * * @return {number} number of items. */ tart.Pagination.prototype.getTotalItems = function() { return this.totalItems_; }; /** * Set number of items * * @param {number} itemCount number of items. * @return {tart.Pagination} . */ tart.Pagination.prototype.setTotalItems = function(itemCount) { this.totalItems_ = itemCount; this.totalPage_ = Math.ceil(itemCount / this.itemPerPage_); return this; }; /** * Get number of items to be listed in a page * * @return {number} number of items in a page. */ tart.Pagination.prototype.getItemPerPage = function() { return this.itemPerPage_; }; /** * set number of items to be listed in a page * * @param {number} itemPerPage number of items in a page. * @return {tart.Pagination} . */ tart.Pagination.prototype.setItemPerPage = function(itemPerPage) { this.itemPerPage_ = itemPerPage; return this; }; /** * Determine if next page is available * * @return {boolean} is next page available. */ tart.Pagination.prototype.hasNext = function() { return this.currentPage + 1 <= this.totalPage_; }; /** * Get next page * * @return {number} next page number. */ tart.Pagination.prototype.getNext = function() { return this.hasNext() ? this.currentPage + 1 : this.currentPage; }; /** * Determine if previous page is available * * @return {boolean} is previous page available. */ tart.Pagination.prototype.hasPrev = function() { return this.currentPage - 1 >= 1; }; /** * Get previous page * * @return {number} previous page number. */ tart.Pagination.prototype.getPrev = function() { return this.hasPrev() ? this.currentPage - 1 : this.currentPage; }; /** * Change page to next page */ tart.Pagination.prototype.next = function() { var oldValue = this.currentPage; this.currentPage = this.getNext(); this.triggerPageChange_(oldValue, this.currentPage); }; /** * Change page to previous page */ tart.Pagination.prototype.prev = function() { var oldValue = this.currentPage; this.currentPage = this.getPrev(); this.triggerPageChange_(oldValue, this.currentPage); }; ================================================ FILE: tart/Pagination/spec/PaginationSpec.js ================================================ goog.require('tart.Pagination'); goog.provide('tart.Pagination.SpecRunner'); describe('Pagination', function() { var paginator; beforeEach(function() { paginator = new tart.Pagination(); }); describe('should contain required parameters', function() { it('should have totalPage', function() { expect(paginator.getTotalPage()).toBeGreaterThan(0); }); it('should have currentPage', function() { expect(paginator.getCurrentPage()).toBeGreaterThan(0); }); it('should have itemPerPage', function() { expect(paginator.getItemPerPage()).toBeGreaterThan(0); }); it('should have totalItems', function() { expect(paginator.getTotalItems()).toBeGreaterThan(0); }); it('should set totalPage', function() { paginator.setTotalPage(5); expect(paginator.getTotalPage()).toEqual(5); }); it('should set currentPage', function() { paginator.setTotalPage(5); paginator.setCurrentPage(5); expect(paginator.getCurrentPage()).toEqual(5); }); it('should set itemPerPage', function() { paginator.setItemPerPage(2); expect(paginator.getItemPerPage()).toEqual(2); }); it('should set totalItems', function() { paginator.setTotalItems(2); expect(paginator.getTotalItems()).toEqual(2); }); it('should change page count when totalItem count set', function() { paginator.setItemPerPage(2); paginator.setTotalItems(5); expect(paginator.getTotalPage()).toEqual(3); }); it('should change item count when totalPage count set', function() { paginator.setItemPerPage(2); paginator.setTotalPage(4); expect(paginator.getTotalItems()).toEqual(8); }); it('should set page count to totalPageCount if page > totalPageCount', function() { paginator.setTotalPage(5); paginator.setCurrentPage(6); expect(paginator.getCurrentPage()).toEqual(5); }); it('should set page count to 1 if page < 1', function() { paginator.setTotalPage(5); paginator.setCurrentPage(0); expect(paginator.getCurrentPage()).toEqual(1); }); }); describe('controls navigation', function() { it('should have a next element if currentPage < totalPage', function() { paginator.setCurrentPage(3); paginator.setTotalPage(4); expect(paginator.hasNext()).toBeTruthy(); }); it('should not have a next element if currentPage >= totalPage', function() { paginator.setTotalPage(4).setCurrentPage(4); paginator.setTotalPage(4); expect(paginator.hasNext()).toBeFalsy(); }); it('should have a previous element if currentPage > 1', function() { paginator.setTotalPage(4).setCurrentPage(2); expect(paginator.hasPrev()).toBeTruthy(); }); it('should not have a next element if currentPage <= 1', function() { paginator.setCurrentPage(1); paginator.setTotalPage(4); expect(paginator.hasPrev()).toBeFalsy(); }); }); describe('triggers pageChanged event on some conditions', function() { it('should trigger event on setCurrentPage', function() { var triggeredObject = {}; goog.events.listen(paginator, tart.Pagination.EventTypes.PAGE_CHANGED, function(e) { triggeredObject = e; }); paginator.setCurrentPage(10); //check if newValue and oldValue exists expect(triggeredObject.oldValue && triggeredObject.newValue).toBeTruthy(); }); it('should trigger event on next()', function() { paginator.setTotalPage(12); paginator.setCurrentPage(10); var triggeredObject = {}; goog.events.listen(paginator, tart.Pagination.EventTypes.PAGE_CHANGED, function(e) { triggeredObject = e; }); paginator.next(); //check if newValue and oldValue exists expect(triggeredObject.oldValue && triggeredObject.newValue).toBeTruthy(); }); it('should trigger event on prev()', function() { paginator.setTotalPage(12); paginator.setCurrentPage(10); var triggeredObject = {}; goog.events.listen(paginator, tart.Pagination.EventTypes.PAGE_CHANGED, function(e) { triggeredObject = e; }); paginator.prev(); //check if newValue and oldValue exists expect(triggeredObject.oldValue && triggeredObject.newValue).toBeTruthy(); }); }); }); /** * Run jasmine spec */ tart.Pagination.SpecRunner = function() { jasmine.getEnv()['addReporter'](new jasmine.TrivialReporter()); jasmine.getEnv()['execute'](); }(); ================================================ FILE: tart/Pagination/spec/SpecRunner.html ================================================ Jasmine Test Runner ================================================ FILE: tart/Registry.js ================================================ // Copyright (c) 2009-2010 Techinox Information Technologies (http://www.techinox.com) // Techinox Commercial License // // @author Armagan Amcalar /** * @fileoverview tart.Registry is a convenience class that wraps goog.structs.Map and is intended to use as * a global registry across a web application. */ goog.provide('tart.Registry'); goog.require('goog.structs.Map'); /** * tart Registry class. * @constructor * @extends {goog.structs.Map} */ tart.Registry = function() { goog.base(this); }; goog.inherits(tart.Registry, goog.structs.Map); ================================================ FILE: tart/StateMachine/State.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview State class is used in conjunction with tart.StateMachine. It is a value object that holds two * properties; a function that is executed when the state machine is in this state, and a transitions object for * events that may happen in this state. */ goog.provide('tart.State'); /** * Tart State class for state machines. * @constructor * @param {function()} fn The state function to be executed. */ tart.State = function(fn) { this.fn = fn; this.transitions = {}; }; ================================================ FILE: tart/StateMachine/StateMachine.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview This class is an implementation of a Moore state machine that is common in digital electronics, etc. * * The state machine is by it's nature an event-driven system. Events that come at the right state will put the state * machine in another expected state. * * To keep the implementation simple, many details are omitted. The state machine doesn't keep a record of its * states, new states cannot be added after a state machine is started, etc. These rules do not prevent * the functioning of the machine though. * * This class is a base class and not intended to be used on its own. To use a state machine, the developer has to * subclass this base class and override the createStates_ method. This method should hold the * initialization of states in the state machine and should return the first state the machine should begin with. * * This implementation features lazy loading in the sense that the createStates_ method is not called * until the first call to startMachine method. * * Example usage: * * Foo.MooreMachine = function(){ * goog.base(this); * } * goog.inherits(Foo.MooreMachine, tart.StateMachine); * * // Having an enumerated type for events helps readability * Foo.MooreMachine.Events = { * CLICK: '0', * DOUBLECLICK: '1' * } * * Foo.MooreMachine.prototype.createStates_ = function(){ * var STATE1 = new tart.State(function(){ * console.log("state 1"); * }); * * var STATE2 = new tart.State(function(){ * console.log("state 2"); * }); * * STATE1.transitions[Foo.MooreMachine.Events.CLICK] = STATE2; * STATE2.transitions[Foo.MooreMachine.Events.DOUBLECLICK] = STATE1; * * this.addState_(STATE1); * this.addState_(STATE2); * * return STATE1; * } * * var myMachine = new Foo.MooreMachine(); * myMachine.startMachine(); * */ goog.require('goog.array'); goog.require('goog.pubsub.PubSub'); goog.require('tart.State'); goog.provide('tart.StateMachine'); /** * Tart State Machine Class * @constructor * @extends {goog.pubsub.PubSub} */ tart.StateMachine = function() { goog.base(this); /** * @type {Array.} * @private */ this.events_ = []; this.currentState = undefined; }; goog.inherits(tart.StateMachine, goog.pubsub.PubSub); /** * Adds a state to the state machine. * @param {tart.State} state State to be added. * @protected */ tart.StateMachine.prototype.addState = function(state) { for (var event in state.transitions) { goog.array.insert(this.events_, event); } }; /** * Sets the current state disregarding the previous one, and executes it's function. * @param {tart.State} state State to be set as the current state. * @param {Array.<*>=} opt_args Arguments to pass to the new state. * @protected */ tart.StateMachine.prototype.setCurrentState = function(state, opt_args) { opt_args = opt_args || []; this.currentState = state; this.currentState.fn.apply(this, opt_args); }; /** * Starts the state machine. If it's the first time this function is called, it also lazy loads the states via * createStates method. */ tart.StateMachine.prototype.startMachine = function() { if (this.currentState == undefined) { this.firstState = this.createStates(); this.bindEvents_(); this.setCurrentState(this.firstState); } }; tart.StateMachine.prototype.reset = function() { this.firstState && this.setCurrentState(this.firstState); }; /** * This should be overridden by child classes. It holds the initialization of states and is called when the * startMachine method is called for the first time. * @return {tart.State} The first state that the machine will start with. */ tart.StateMachine.prototype.createStates = goog.abstractMethod; /** * Subscribes its handleEvent_ function to its every event * @private */ tart.StateMachine.prototype.bindEvents_ = function() { var self = this; for (var i = 0, l = self.events_.length; i < l; i++) { var eventName = self.events_[i]; self.subscribe(eventName, self.handleEvent_(self, eventName)); } }; /** * Handles incoming events. If any of the events are in the transitions list for the current state, that transition's * target state becomes the current state. * @param {tart.StateMachine} self The State Machine instance (due to the nature of goog.pubsub.PubSub.subscribe, * this function loses its scope. This variable corrects it). * @param {string} event The event to handle. * @return {function()} Returns a closure to use as an eventHandler. * @private */ tart.StateMachine.prototype.handleEvent_ = function(self, event) { return function() { var nextState = self.currentState.transitions[event]; if (nextState) { self.setCurrentState(nextState, Array.prototype.slice.call(arguments)); } } }; ================================================ FILE: tart/Tabs/TabPanel.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Creates an instance with a tab and panel for using with an existing tabpanel. * * Example Usage: * * var panel1 = new tart.TabPanel({ * title: 'Tab 1', * html: 'This is a basic text' * }); * */ goog.provide('tart.TabPanel'); goog.require('goog.pubsub.PubSub'); /** * Returns an instance of tab and panel for using it in an tabpanel * Options object should include a title as tab's title and html as panel's default html * If Options object is ommited, an empty string will appended as tab title and panel html * @constructor * @extends {goog.pubsub.PubSub} * @param {Object=} options Customized options for panel instance. */ tart.TabPanel = function(options) { goog.base(this); this.initOptions(options); this.$tab = $(this.templates_tab()); this.$panel = $(this.templates_panel()); }; goog.inherits(tart.TabPanel, goog.pubsub.PubSub); /** * Merges custom options object with defaults * @protected * @param {Object=} options Custom config object. */ tart.TabPanel.prototype.initOptions = function(options) { this.props = {}; options = options || {}; this.props.panelCssClass = options['panelCssClass'] || ''; this.props.tabCssClass = options['tabCssClass'] || ''; this.props.title = options.title || ''; this.props.html = options.html || ''; }; /** * @return {string} Tab markup. */ tart.TabPanel.prototype.templates_tab = function() { return '
' + this.props.title + '
'; }; /** * @return {string} Panel markup. */ tart.TabPanel.prototype.templates_panel = function() { return '
' + this.props.html + '
'; }; /** * Callback function that will be triggered before a TabPanel instance is shown. * @type {Function|undefined} */ tart.TabPanel.prototype.onShow = undefined; /** * Callback function that will be triggered before a TabPanel instance is shown. * @type {Function|undefined} */ tart.TabPanel.prototype.onClose = undefined; ================================================ FILE: tart/Tabs/Tabs.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Tabs is a fully working and customizable tab panel creator class. * * Example Usage: * * * var tabPanel = new tart.Tabs({ * activeTab: 1 * }); * * var panel1 = new tart.TabPanel({ * title: 'Tab 1', * html: 'This is a basic text' * }); * var panel2 = new tart.TabPanel({ * title: 'Tab 2', * html: 'Lorem ipsum dolor sit amet' * }); * var panel3 = new tart.TabPanel({ * title: 'Tab 3', * html: 'Lorem ipsum dolor sit amet is a dummy text' * }); * * tabPanel.addTabPanel(panel1); * tabPanel.addTabPanel(panel2); * tabPanel.addTabPanel(panel3); * * var dom = tabPanel.buildDOM(); * $('body').append(dom); * */ goog.provide('tart.Tabs'); goog.require('goog.pubsub.PubSub'); goog.require('tart.TabPanel'); /** * Tabs is a fully working and customizable tab panel creator class * @constructor * @param {Object=} options Customized initial options for tabpanel. * @extends {goog.pubsub.PubSub} */ tart.Tabs = function(options) { goog.base(this); options = options || {}; this.initOptions(options); this.$tabContainer = $(this.templates_tabContainer()); this.$panelContainer = $(this.templates_panelContainer()); /** * Holds TabPanel objects associated with the tabpanel * @type {Array.} * @protected */ this.tabPanels = []; /** * State of tab panel * @protected */ this.rendered = false; this.initialize(this.props.renderConfig); }; goog.inherits(tart.Tabs, goog.pubsub.PubSub); /** * Initialize the tabpanel with calling buildDOM method * @param {!string} renderCfg Where to container will be appended. */ tart.Tabs.prototype.initialize = function(renderCfg) { var panelDOM = this.buildDOM(); if (renderCfg.insertAfter) { panelDOM.insertAfter(renderCfg.insertAfter); } else if (renderCfg.insertBefore) { panelDOM.insertBefore(renderCfg.insertBefore); } else if (renderCfg.append) { renderCfg.append.append(panelDOM); } }; /** * Adds new tab to existing tab panel * @param {!(Array|tart.TabPanel)} tabPanels An array that holds instance of TabPanel Class to add new tab. * Also tabPanels argument should be a tab panel instance instead of array. */ tart.Tabs.prototype.addTabPanel = function(tabPanels) { var that = this; if (goog.isArray(tabPanels)) { for (var i = 0, len = tabPanels.length; i < len; i++) { that.addTab(tabPanels[i]); } } else { that.addTab(tabPanels); } this.setActiveTab(this.activeTab); }; /** * Adds a new tab to a Tabs instance. * @param {tart.TabPanel} tabPanel instance that's going to be added to a Tabs instance. */ tart.Tabs.prototype.addTab = function(tabPanel) { this.tabPanels.push(tabPanel); this.$tabContainer.append(tabPanel.$tab); this.$panelContainer.append(tabPanel.$panel); this.bindTabPanelEvents(tabPanel); }; /** * Removes the tab with the given index. * @param {!number} index Index number of the tab that you want to remove. */ tart.Tabs.prototype.removeTab = function(index) { var tabsLength = this.getTabsLength(); if (index >= tabsLength || tabsLength === 0) { return; } this.tabPanels[index].$tab.remove(); this.tabPanels[index].$panel.remove(); this.tabPanels.splice(index, 1); try { if (index <= this.activeTab) { if (index === this.activeTab) { this.setActiveTab(index - 1); } else { this.setActiveTab(this.activeTab - 1); } } } catch (e) {} }; /** * Sets the tab as active with the given index. * @param {!number} index Index number of the tab that you want to set as active. */ tart.Tabs.prototype.setActiveTab = function(index) { if (index >= this.getTabsLength()) { return false; } var deActivatedTab = this.getActiveTab(); for (var i = 0, len = this.tabPanels.length; i < len; i++) { if (index != i) { this.tabPanels[i].$tab.removeClass(this.props.activeTabCssClass); this.tabPanels[i].$panel.removeClass(this.props.activePanelCssClass); } } this.tabPanels[index].$tab.addClass(this.props.activeTabCssClass); this.tabPanels[index].$panel.addClass(this.props.activePanelCssClass); this.activeTab = index; var newlyActivatedTab = this.getActiveTab(); if (deActivatedTab.onClose && deActivatedTab != newlyActivatedTab) /* prevent double fn call on initialize */ deActivatedTab.onClose(this); newlyActivatedTab.onShow && newlyActivatedTab.onShow(this); // call newly activated tab's onShow function this.publish('tabChange', this.getActiveTab(), this); }; /** * Returns the active tab object contains tab and panel elements and templates that used in tab and panel. * @return {tart.TabPanel} The active tab. */ tart.Tabs.prototype.getActiveTab = function() { return this.tabPanels[this.activeTab]; }; /** * Binds required events of tabpanel. * @param {!tart.TabPanel} tabPanel Tabpanel instance for binding events to it. */ tart.Tabs.prototype.bindTabPanelEvents = function(tabPanel) { var that = this; var setMeActive = function() { var myIndex = goog.array.indexOf(that.tabPanels, tabPanel); that.setActiveTab(myIndex); }; tabPanel.$tab.click(setMeActive); }; /** * Builds DOM elements for tabpanel and returns DOM as ready to append to a container * @return {jQueryObject} DOM element of the Tabs instance. */ tart.Tabs.prototype.buildDOM = function() { this.$dom = $(this.templates_dom()); this.$dom.append(this.$tabContainer); this.$dom.append(this.$panelContainer); this.rendered = true; return this.$dom; }; /** * @return {boolean} The status of tabpanel, rendered or not. */ tart.Tabs.prototype.isRendered = function() { return this.rendered; }; /** * @return {number} number of tabs associated with the Tabs instance. */ tart.Tabs.prototype.getTabsLength = function() { return this.tabPanels.length; }; /** * Merges custom options object with defaults * @protected * @param {Object=} options Customized options for Tabs instance. */ tart.Tabs.prototype.initOptions = function(options) { var props = this.props = {}; props.axis = options.axis || 'x'; this.activeTab = options.activeTab || 0; props.renderConfig = options.renderConfig || {}; props.tabPanelCssClass = options.tabPanelCssClass || ''; props.tabContainerCssClass = options.tabContainerCssClass || ''; props.panelContainerCssClass = options.panelContainerCssClass || ''; props.activeTabCssClass = options.activeTabCssClass || 'tartActiveTab'; props.activePanelCssClass = options.activePanelCssClass || 'tartActivePanel'; props.panelWrapperCssClass = options.panelWrapperCssClass || ''; }; /** * @return {string} Main container markup. */ tart.Tabs.prototype.templates_dom = function() { return '
'; }; /** * @return {string} Tab container markup. */ tart.Tabs.prototype.templates_tabContainer = function() { return '
'; }; /** * @return {string} Panel container markup. */ tart.Tabs.prototype.templates_panelContainer = function() { return '
'; }; ================================================ FILE: tart/Validation/README.md ================================================ # Tart Validator A JavaScript validation library ## Testing specs You need [jasmine](http://pivotal.github.com/jasmine/) to run the specs You better have [qasmine](https://github.com/tart/qasmine) to automatize the testing process ### How to run qasmine Simlpy, run qasmine spec/SpecRunner.html ## Validation checks * is.email(string) : Checks string as a valid email * is.notOnlySpace(string) : Checks string that it doesnt contains only from whitespaces * is.numeric(string) : Checks string that only contains numeric values * is.digitAndNonDigit(string) : Checks string which contains both digits and non-digit chars, can be useful to check passwords' strength * has.maxLength(string, value) : Checks string for max length * has.minlength(string, value) : Checks string for min length * has.minValue(number, value) : Cheks number for its min value * has.maxValue(number, value) : Cheks number for its max value ================================================ FILE: tart/Validation/Validation.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.Validation is a validation library which checks for variable's format (such as email, number) or * attribute (such as char length). * * Example usage: * * var validator = tart.Validation; * var isValidEmail = validator.is.email("foo@bar.com"); //returns boolean true * var isNumeric = validator.is.numeric("123foo"); // returns boolean false * var hasMaxLength10 = validator.has.maxLength("foobar", 10); //returns boolean true * * * More examples can be seen from spec/ValidationSpec.js file */ goog.provide('tart.Validation'); goog.provide('tart.Validation.has'); goog.provide('tart.Validation.is'); /** * Checks if given text is valid email * * @param {string} text email text to be validated. * @return {boolean} true if its valid email. */ tart.Validation.is.email = function(text) { var pattern = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/; return pattern.test(text); }; /** * Checks if a value is true. * * @param {boolean} value value to check. * @return {boolean} true if value evaluates to true. */ tart.Validation.is.truthy = function(value) { return value == true; }; /** * Checks if given text doesnt contains only white spaces but some chars or numbers * * @param {string} text text to be validated. * @return {boolean} true if text contains any char or number. */ tart.Validation.is.notOnlySpace = function(text) { var result = text.replace(/^\s+|\s+$/g, '').length > 0; return result; }; /** * Checks if given text contains only numeric chars * * @param {string} text text to be validated. * @return {boolean} true if text contains only numbers. */ tart.Validation.is.numeric = function(text) { var pattern = /^[0-9]+$/; return pattern.test(text); }; /** * Checks if given text contains both digit and non-digit chars * * @param {string} text text to be validated. * @return {boolean} true if text contains both digit and non-digit chars. */ tart.Validation.is.digitAndNonDigit = function(text) { var pattern = /(\d\D)|(\D\d)/; return pattern.test(text); }; /** * Checks if both given arguments are equal * * @param {*} arg1 item to be validated. * @param {*} arg2 item to be validated. * @return {boolean} true if both arguments are equal. */ tart.Validation.is.equal = function(arg1, arg2) { return arg1 == arg2; }; /** * Checks for strings' min length * * @param {string} text string to check for char length. * @param {number} value char length value. * @return {boolean} true if string's length > value. */ tart.Validation.has.minLength = function(text, value) { return (text.length >= value); }; /** * Checks for string's max length * * @param {string} text string to check for char length. * @param {number} value char length value. * @return {boolean} true if string's length < value. */ tart.Validation.has.maxLength = function(text, value) { return (text.length <= value); }; /** * Checks for string or number's min numeric value * * @param {string|number} num number to check value for. * @param {number} value value to check. * @return {boolean} true if string's num < value. */ tart.Validation.has.minValue = function(num, value) { return num >= value; }; /** * Checks for string or number's max numeric value * * @param {string|number} num number to check value for. * @param {number} value value to check. * @return {boolean} true if string's num > value. */ tart.Validation.has.maxValue = function(num, value) { return num <= value; }; ================================================ FILE: tart/Validation/spec/SpecRunner.html ================================================ Jasmine Test Runner ================================================ FILE: tart/Validation/spec/ValidationSpec.js ================================================ goog.require('tart.Validation'); goog.provide('tart.Validation.SpecRunner'); describe('Validation', function() { var validator; beforeEach(function() { validator = tart.Validation; }); it('validator should be object', function() { expect(typeof validator).toEqual('object'); }); describe('Validation.is', function() { describe('Validation.is.email', function() { it('regular email like alnum1@alnum2.tld should pass', function() { var result = validator.is.email('alnum1@alnum2.tld'); expect(result).toBeTruthy(); }); it('should not validate an email which has alnum1@alnum2.tldmorethan4chars', function() { var result = validator.is.email('alnum1@alnum2.tldmorethan4chars'); expect(result).toBeFalsy(); }); it('should not validate an email which containts non@alhanümeric.çars ', function() { var result = validator.is.email('non@alhanümeric.çars'); expect(result).toBeFalsy(); }); it('should validate an email which belongs@to.a.sub.dom.ain', function() { var result = validator.is.email('belongs@to.a.sub.dom.ain'); expect(result).toBeTruthy(); }); it('should not validate email which contains@space.com', function() { var result = validator.is.email('which contains@space.com'); expect(result).toBeFalsy(); }); }); describe('Validation.is.notOnlySpace', function() { it('should not validate an input which has only white space', function() { var result = validator.is.notOnlySpace(' '); expect(result).toBeFalsy(); }); it('should validate an input which starts with whitespace but has other chars', function() { var result = validator.is.notOnlySpace(' foobar'); expect(result).toBeTruthy(); }); it('should validate an input which starts and ends with whitespace but has other chars', function() { var result = validator.is.notOnlySpace(' foobar '); expect(result).toBeTruthy(); }); }); describe('Validation.is.numeric', function() { it('should not validate text which has non numeric chars', function() { var result = validator.is.numeric('foo123bar'); expect(result).toBeFalsy(); }); it('should not validate text which starts with numeric but has non numeric chars', function() { var result = validator.is.numeric('123bar'); expect(result).toBeFalsy(); }); it('should not validate text which starts with space but has only numeric chars', function() { var result = validator.is.numeric(' 123'); expect(result).toBeFalsy(); }); it('should validate text which has only numeric chars', function() { var result = validator.is.numeric('123'); expect(result).toBeTruthy(); }); }); //TODO: add more tests describe('Validation.is.equal', function() { it('should validate same strings', function () { var result = validator.is.equal("osman", "osman"); expect(result).toBeTruthy(); }); it('should validate same numbers', function () { var result = validator.is.equal(1453, 1453); expect(result).toBeTruthy(); }); it('should validate numeric strings', function () { var result = validator.is.equal("1453", 1453); expect(result).toBeTruthy(); }); it('should validate empty strings', function () { var result = validator.is.equal("", ""); expect(result).toBeTruthy(); }); it('should validate falsy values', function () { var result = validator.is.equal("", false); expect(result).toBeTruthy(); }); it('should not validate falsy values with null', function () { var result = validator.is.equal(null, false); expect(result).toBeFalsy(); }); }); describe('Validation.is.digitAndNonDigit', function() { it('should not validate text which has chars but numbers', function() { var result = validator.is.digitAndNonDigit('foobar'); expect(result).toBeFalsy(); }); it('should not validate text which has chars only numbers', function() { var result = validator.is.digitAndNonDigit('1234'); expect(result).toBeFalsy(); }); it('should not validate text which has only whitespaces', function() { var result = validator.is.digitAndNonDigit(' '); expect(result).toBeFalsy(); }); it('should validate text which contains both numbers and chars', function() { var result = validator.is.digitAndNonDigit('foo123bar'); expect(result).toBeTruthy(); }); it('should validate text which contains both numbers, chars and whitespaces', function() { var result = validator.is.digitAndNonDigit('foo 123 bar'); expect(result).toBeTruthy(); }); }); }); describe('Validation.has', function() { describe('Validation.has.minLength', function() { it("should validate 'abc' has minLength 3", function() { var result = validator.has.minLength('abc', 3); expect(result).toBeTruthy(); }); it("should validate 'abc' has minLength 2", function() { var result = validator.has.minLength('abc', 2); expect(result).toBeTruthy(); }); it("should not validate 'abc' has minLength 4", function() { var result = validator.has.minLength('abc', 4); expect(result).toBeFalsy(); }); it("should validate ' ' has minLength 1", function() { var result = validator.has.minLength(' ', 1); expect(result).toBeTruthy(); }); it("should not validate '' has minLength 1", function() { var result = validator.has.minLength('', 1); expect(result).toBeFalsy(); }); }); describe('Validation.has.maxLength', function() { it("should not validate 'abc' has max length 2", function() { var result = validator.has.maxLength('abc', 2); expect(result).toBeFalsy(); }); it("should validate 'ab' has max length 2", function() { var result = validator.has.maxLength('ab', 2); expect(result).toBeTruthy(); }); it("should not validate ' ' has max length 2", function() { var result = validator.has.maxLength(' ', 2); expect(result).toBeFalsy(); }); it("should validate '' has max length 0", function() { var result = validator.has.maxLength('', 0); expect(result).toBeTruthy(); }); }); describe('Validation.has.minValue', function() { it('should validate 3 has min value 1', function() { var result = validator.has.minValue(3, 1); expect(result).toBeTruthy(); }); it('should not validate 3 has min value 4', function() { var result = validator.has.minValue(3, 4); expect(result).toBeFalsy(); }); }); describe('Validation.has.maxValue', function() { it('should validate 3 has max value 4', function() { var result = validator.has.maxValue(3, 4); expect(result).toBeTruthy(); }); it('should not validate 3 has max value 2', function() { var result = validator.has.maxValue(3, 2); expect(result).toBeFalsy(); }); }); }); }); /** * Run jasmine spec */ tart.Validation.SpecRunner = function() { jasmine.getEnv()['addReporter'](new jasmine.TrivialReporter()); jasmine.getEnv()['execute'](); }(); ================================================ FILE: tart/XhrManager.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.XhrManager static class to handle XHR requests. */ goog.provide('tart.XhrManager'); /** * jQuery GET's wrapper * * @param {string} url url to send request. * @param {Object} params POST/GET parameters. * @param {?function(Object)} success success callback. * @param {?function(Object)=} opt_fail fail callback. * @param {string=} opt_dataType data type. * @return {Object} . */ tart.XhrManager.get = function(url, params, success, opt_fail, opt_dataType) { return tart.XhrManager.ajax('GET', url, params, success, opt_fail, opt_dataType); }; /** * jQuery POST's wrapper * * @param {string} url url to send request. * @param {Object} params POST/GET parameters. * @param {?function(Object)} success success callback. * @param {?function(Object)=} opt_fail fail callback. * @param {string=} opt_dataType data type. * @return {Object} . */ tart.XhrManager.post = function(url, params, success, opt_fail, opt_dataType) { return tart.XhrManager.ajax('POST', url, params, success, opt_fail, opt_dataType); }; /** * jQuery POST's wrapper * * @param {string} type request type. * @param {string} url url to send request. * @param {Object} params POST/GET parameters. * @param {?function(Object)} success success callback. * @param {?function(Object)=} opt_fail fail callback. * @param {string=} opt_dataType data type. * @return {Object} . */ tart.XhrManager.ajax = function(type, url, params, success, opt_fail, opt_dataType) { return $.ajax({ 'type': type, 'url': url, 'data': params, 'dataType': opt_dataType || 'json', 'xhrFields': { 'withCredentials': true }, 'success': function(response) { success && success(response); }, 'error': function(response) { opt_fail && opt_fail(response); } }); }; ================================================ FILE: tart/base/Model.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.base.Model base model. */ goog.provide('tart.base.Model'); goog.require('goog.structs.Map'); goog.require('goog.debug.ErrorHandler'); goog.require('goog.events.EventHandler'); goog.require('goog.events.EventTarget'); /** * Base model * * @extends {goog.events.EventTarget} * @constructor */ tart.base.Model = function() { goog.events.EventTarget.call(this); /** @private **/ this.items_ = null; /** @private **/ this.totalItemCount_ = 0; this.params = new goog.structs.Map(); }; goog.inherits(tart.base.Model, goog.events.EventTarget); /** * Abstract method to load data from any resource * @param {Function=} opt_callback method after load. */ tart.base.Model.prototype.load = function (opt_callback) { goog.abstractMethod(); }; /** * getter for items * @param {boolean=} dispatchEvent Whether this method should throw an event when its items are loaded. This may be used in classes that * extend tart.base.Model. * @return {Object} items all items. */ tart.base.Model.prototype.getItems = function(dispatchEvent) { return this.items_; }; /** * Setter for items * @param {Object} items items to be set. */ tart.base.Model.prototype.setItems = function(items) { this.items_ = items; }; /** * Set total item count * @param {number} itemCount total item count for this model. */ tart.base.Model.prototype.setTotalItemCount = function(itemCount) { this.totalItemCount_ = itemCount; }; /** * Get total item count * @return {number} total item count for this model. */ tart.base.Model.prototype.getTotalItemCount = function() { return this.totalItemCount_; }; /** @typedef {{type: string, oldValue, newValue}} */ tart.base.Model.Event; /** * Overriding goog.events.EventTarget's dispatchEvent method, to make this event consistent in application * * @param {Object|string} modelEvent event object which has type, oldValue and newValue fields. * @return {boolean} . * @override */ tart.base.Model.prototype.dispatchEvent = function(modelEvent) { return goog.base(this, 'dispatchEvent', modelEvent); }; ================================================ FILE: tart/base/plugin/BasePlugin.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.base.plugin.BasePlugin to get common properties for a plugin in one class. */ goog.provide('tart.base.plugin.BasePlugin'); goog.require('goog.debug.ErrorHandler'); goog.require('goog.events.EventHandler'); goog.require('goog.events.EventTarget'); goog.require('goog.structs.Map'); /** * @param {tart.base.Model} model * @extends {goog.events.EventTarget} * @constructor */ tart.base.plugin.BasePlugin = function (model) { goog.events.EventTarget.call(this); /** @protected */ this.model = model; /** @protected **/ this.map = new goog.structs.Map(); this.model.params.set(this.key, this.map); }; goog.inherits(tart.base.plugin.BasePlugin, goog.events.EventTarget); /** * clear map for plugin */ tart.base.plugin.BasePlugin.prototype.clear = function () { this.map.clear(); }; /** * Getter for model */ tart.base.plugin.BasePlugin.prototype.getModel = function () { return this.model; }; /** * models key */ tart.base.plugin.BasePlugin.prototype.key = undefined; ================================================ FILE: tart/base/plugin/Filter.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.base.plugin.Filter model filter plugin. */ goog.provide('tart.base.plugin.Filter'); goog.require('tart.base.plugin.BasePlugin'); /** * @param {tart.base.Model} model * * @extends {tart.base.plugin.BasePlugin} * @constructor */ tart.base.plugin.Filter = function (model) { goog.base(this, model); }; goog.inherits(tart.base.plugin.Filter, tart.base.plugin.BasePlugin); /** * Set plugins param key */ tart.base.plugin.Filter.prototype.key = "filterParams"; /** * @param {string} field field to be filtereded. * @param {string} condition condition operator. * @param {string} value field value to filter field for. */ tart.base.plugin.Filter.prototype.addFilter = function (field, condition, value) { /** * There can be multiple condition-value pair for a field */ var fieldFilter = this.map.get(field); //and if this field did not set before create a new object if (!fieldFilter) { fieldFilter = {}; } fieldFilter[condition] = value; this.map.set(field, fieldFilter); }; ================================================ FILE: tart/base/plugin/Pager.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.base.plugin.Pager model pager plugin to handle pagination. */ goog.provide('tart.base.plugin.Pager'); goog.require('tart.Pagination'); goog.require('tart.base.plugin.BasePlugin'); /** * @param {tart.base.Model} model tart.base.Model instance to set pager params. * @param {tart.Pagination=} pagination optional tart.Pagination instance to handle pagination. * * @extends {tart.base.plugin.BasePlugin} * @constructor */ tart.base.plugin.Pager = function(model, pagination) { goog.base(this, model); var that = this; /** @private */ that.pagination_ = pagination || new tart.Pagination(); that.pagination_.setParentEventTarget(this); /** * Change offset on page change events */ goog.events.listen(that.pagination_, tart.Pagination.EventTypes.PAGE_CHANGED, function(e) { var limit = parseInt(that.map.get('limit'), 10); var newOffset = (e.newValue - 1) * limit; that.map.set('offset', newOffset); that.model.params.set(that.key, that.map); }); }; goog.inherits(tart.base.plugin.Pager, tart.base.plugin.BasePlugin); /** * Plugin's parameter key which is inherited from BasePlugin and should be defined */ tart.base.plugin.Pager.prototype.key = 'paginationParams'; /** * @param {number} pageCount number of pages. */ tart.base.plugin.Pager.prototype.setTotalPageCount = function(pageCount) { this.map.set('pageCount', pageCount); }; /** * @param {number} offset cursor start point for paging. */ tart.base.plugin.Pager.prototype.setOffset = function(offset) { this.map.set('offset', offset); }; /** * * @param {number} limit item count to fetch. */ tart.base.plugin.Pager.prototype.setLimit = function(limit) { this.map.set('limit', limit); this.pagination_.setItemPerPage(limit); }; /** * * @return {number} current limit. */ tart.base.plugin.Pager.prototype.getLimit = function() { return this.pagination_.getItemPerPage(); }; /** * @param {number} totalItemCount set total item count for paginator. */ tart.base.plugin.Pager.prototype.setTotalItems = function(totalItemCount) { this.pagination_.setTotalItems(totalItemCount); }; /** * Next wrapper for paginator. */ tart.base.plugin.Pager.prototype.next = function() { this.pagination_.next(); }; /** * Prev wrapper for paginator. */ tart.base.plugin.Pager.prototype.prev = function() { this.pagination_.prev(); }; /** * setCurrentPage wrapper for paginator. * @param {number} currentPageNum current page number. */ tart.base.plugin.Pager.prototype.setCurrentPage = function(currentPageNum) { this.pagination_.setCurrentPage(currentPageNum); }; /** * @return {number} number of pages. */ tart.base.plugin.Pager.prototype.getTotalPage = function() { return this.pagination_.getTotalPage(); }; /** * @return {number} current page number. */ tart.base.plugin.Pager.prototype.getCurrentPage = function() { return this.pagination_.getCurrentPage(); }; /** * @return {boolean} Whether there is a previous page available. */ tart.base.plugin.Pager.prototype.hasPrev = function() { return this.pagination_.hasPrev(); }; /** * @return {boolean} Whether there is a next page available. */ tart.base.plugin.Pager.prototype.hasNext = function() { return this.pagination_.hasNext(); }; ================================================ FILE: tart/base/plugin/Sorter.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.base.plugin.Sorter model sorter plugin. */ goog.provide('tart.base.plugin.Sorter'); goog.require('tart.base.plugin.BasePlugin'); /** * @param {tart.base.Model} model * * @extends {tart.base.plugin.BasePlugin} * @constructor */ tart.base.plugin.Sorter = function (model) { goog.events.EventTarget.call(this); /** @protected */ this.model = model; /** @protected **/ this.sorts = []; this.model.params.set(this.key, this.sorts); }; goog.inherits(tart.base.plugin.Sorter, tart.base.plugin.BasePlugin); /** * Set plugin's param */ tart.base.plugin.Sorter.prototype.key = "sortParams"; /** * @param {string} field field to be sorted. * @param {string} order order by directive, which is asc or desc. */ tart.base.plugin.Sorter.prototype.addSort = function (field, order) { /** * There can be multiple condition-value pair for a field */ var fieldSorter = goog.array.find(this.sorts, function(item){ return goog.object.getAnyKey(item) == field; }); //and if this field did not set before create a new object if (!fieldSorter) { fieldSorter = {}; } fieldSorter[field] = order; this.model.params.get(this.key).push(fieldSorter); }; /** * clear map for plugin */ tart.base.plugin.Sorter.prototype.clear = function () { this.sorts = []; this.model.params.set(this.key, this.sorts); }; ================================================ FILE: tart/components/Carousel/Controller.js ================================================ // Copyright 2012 Tart. All Rights Reserved. // // @author Firat Yalavuz firat.yalavuz@tart.com.tr /** * @fileoverview tart.components.Carousel.Controller is a base class for all carousel Controller's. */ goog.require('goog.events.EventTarget'); goog.require('goog.events.EventTarget'); goog.require('tart.Pagination'); goog.require('tart.base.plugin.Pager'); goog.require('tart.components.Carousel.Model'); goog.require('tart.components.Carousel.Template'); goog.require('tart.components.Carousel.View'); goog.require('tart.components.Controller'); goog.provide('tart.components.Carousel.Controller'); /** * Example controller class * * @extends {tart.components.Controller} * * @constructor */ tart.components.Carousel.Controller = function() { this.model = this.model || new this.modelClass(); this.modelPager = new tart.base.plugin.Pager(this.model, new this.paginationClass()); this.modelPager.setOffset(0); this.modelPager.setLimit(this.itemCount); this.view = new this.viewClass(); this.buildDOM(); this.bindEvents(); }; goog.inherits(tart.components.Carousel.Controller, tart.components.Controller); /** * Carousel's View Class */ tart.components.Carousel.Controller.prototype.viewClass = tart.components.Carousel.View; /** * Carousel's Model Class */ tart.components.Carousel.Controller.prototype.modelClass = tart.components.Carousel.Model; /** * Carousel's Pagination Class */ tart.components.Carousel.Controller.prototype.paginationClass = tart.Pagination; /** * Number of carousel items */ tart.components.Carousel.Controller.prototype.itemCount = 5; /** * Build carousel after items' loaded * @param {Object} visibleItems visible items. * @param {number} totalItemCount total item count. */ tart.components.Carousel.Controller.prototype.buildCarouselAction = function(visibleItems, totalItemCount) { if (visibleItems.length == 0) this.view.noResults(); else { //build carousel this.view.buildCarouselItems(visibleItems); //build pagination this.modelPager.setTotalItems(totalItemCount); if (totalItemCount > this.itemCount) { this.view.buildPager(this.modelPager); } } this.view.handleNavigationButtons(this.modelPager.hasNext(), this.modelPager.hasPrev()); }; /** * Go to page in given direction * @param {string} direction move direction. * @param {Number} pageNumber page number to go. */ tart.components.Carousel.Controller.prototype.goToPageAction = function(direction, pageNumber) { var items = this.model.getItems(); this.view.move(direction, items, this.modelPager.getLimit()); this.view.setPageSelected(pageNumber); }; /** * move next page */ tart.components.Carousel.Controller.prototype.nextAction = function() { this.modelPager.next(); }; /** * move previous page * */ tart.components.Carousel.Controller.prototype.prevAction = function() { this.modelPager.prev(); }; /** * Bind controller events * @protected */ tart.components.Carousel.Controller.prototype.bindEvents = function() { var that = this; //triggered when model.getItems() called goog.events.listen(that.model, tart.components.Carousel.Model.EventTypes.ITEMS_LOADED, function(e) { that.buildCarouselAction(e.visibleItems, e.totalItemCount); if (e.visibleItems.length > 0) { goog.style.setStyle(that.view.getDOM(), 'display', 'block'); } }); //Triggered when page changed goog.events.listen(that.modelPager, tart.Pagination.EventTypes.PAGE_CHANGED, function(e) { //dont do anything if page not changed if (e.oldValue != e.newValue) { var direction; if (e.newValue > e.oldValue) { direction = 'next'; } else { direction = 'prev'; } that.goToPageAction(direction, e.newValue); } that.view.handleNavigationButtons(that.modelPager.hasNext(), that.modelPager.hasPrev()); }); goog.events.listen(that.view.get(that.view.domMappings.NEXT)[0], goog.events.EventType.CLICK, this.nextAction, false, this); goog.events.listen(that.view.get(that.view.domMappings.PREV)[0], goog.events.EventType.CLICK, this.prevAction, false, this); }; ================================================ FILE: tart/components/Carousel/Model.js ================================================ // Copyright 2012 Tart. All Rights Reserved. // // @author Firat Yalavuz firat.yalavuz@tart.com.tr /** * @fileoverview tart.components.Carousel.Model is a base class for all carousel Model's. */ goog.require('goog.events.EventTarget'); goog.require('tart.Carousel'); goog.require('tart.base.Model'); goog.require('tart.dataProxy.CircularLocal'); goog.provide('tart.components.Carousel.Model'); goog.provide('tart.components.Carousel.Model.EventTypes'); /** * All component models should be inherited from goog.events.EventTarget * to publish events to controllers * * @extends {tart.base.Model} * @constructor */ tart.components.Carousel.Model = function() { goog.base(this); /** @protected */ this.proxy = new tart.dataProxy.CircularLocal(); }; goog.inherits(tart.components.Carousel.Model, tart.base.Model); /** * Model's default offset value. */ tart.components.Carousel.Model.prototype.offset = 0; /** * Model's default limit value. */ tart.components.Carousel.Model.prototype.limit = 0; /** * WidgetModel's remoteModelClass's default value is undefined. */ tart.components.Carousel.Model.prototype.remoteModelClass = undefined; /** * WidgetModel's RemoteModelClass's default value is undefined. */ tart.components.Carousel.Model.prototype.remoteModelClassParams = undefined; /** * event types enumaration * @enum {string} */ tart.components.Carousel.Model.EventTypes = { ITEMS_LOADED: 'loaded' }; /** * Get carousel items * @param {boolean} dispatchEvent whether it dispatches an event. * @return {Array} modelItems is count of items.. */ tart.components.Carousel.Model.prototype.getItems = function(dispatchEvent) { var that = this; var modelItems; this.load(function(items) { if (dispatchEvent) { var eventObject = {type: tart.components.Carousel.Model.EventTypes.ITEMS_LOADED, visibleItems: items, totalItemCount: that.getTotalItemCount() }; that.dispatchEvent(eventObject); } modelItems = items; }); return modelItems; }; /** * @param {Function} callback load callback function. */ tart.components.Carousel.Model.prototype.load = function(callback) { var that = this; var remoteModel; var onProxyFetch = function(data) { that.setItems(data); callback.call(this, data); }; var getremoteModelData = function() { var data = remoteModel.getItems(); that.proxy.setData(data); that.setTotalItemCount(data.length); that.proxy.fetch(onProxyFetch); }; that.proxy.setParams(this.params); if (that.proxy.getData()) { that.proxy.fetch(onProxyFetch); } else if (this.remoteModelClass) { remoteModel = new this.remoteModelClass(this.remoteModelClassParams); if (this.pagination) { var remoteModelPager = new tart.base.plugin.Pager(remoteModel, this.pagination); remoteModelPager.setOffset(this.offset); remoteModelPager.setLimit(this.limit); } remoteModel.load(getremoteModelData); } }; ================================================ FILE: tart/components/Carousel/Template.js ================================================ // Copyright 2012 Tart. All Rights Reserved. // // @author Firat Yalavuz firat.yalavuz@tart.com.tr /** * @fileoverview tart.components.Carousel.Template is a base class for all carousel Template's. */ goog.provide('tart.components.Carousel.Template'); /** * @constructor */ tart.components.Carousel.Template = function() { this.properties = { CAROUSEL_WIDTH: this.carouselWidth }; this.domMappings = { NAVIGATION: '.navigation', PREV: '.navigation.prev', NEXT: '.navigation.next', ITEMS: '.contents', PAGER: '.pager', PAGER_ITEMS: '.pagerItems', PAGER_ITEM: 'span' }; }; /** * Header * * @return {string} header markup. */ tart.components.Carousel.Template.prototype.header = function() { return ''; }; tart.components.Carousel.Template.prototype.carouselWidth = 780; /** * Footer * * @return {string} footer markup. */ tart.components.Carousel.Template.prototype.footer = function() { return '
' + '
' + '
'; }; /** * Base markup for carousel * * @return {string} base markup. */ tart.components.Carousel.Template.prototype.base = function() { return ''; }; /** * Markup for carousel group * * @param {Array.<*>} itemArray carousel items. * @return {(Node)} base markup. */ tart.components.Carousel.Template.prototype.carouselItems = goog.abstractMethod; /** * * @param {number} pageNum is number of selected page. * @param {boolean} selected class. * @return {string} markup. */ tart.components.Carousel.Template.prototype.pagerItem = function(pageNum, selected) { var selectedClass = selected ? 'selected' : ''; return ''; }; /** * @return {string} */ tart.components.Carousel.Template.prototype.noResults = function() { return ''; }; ================================================ FILE: tart/components/Carousel/View.js ================================================ // Copyright 2012 Tart. All Rights Reserved. // // @author Firat Yalavuz firat.yalavuz@tart.com.tr /** * @fileoverview tart.components.Carousel.View is a base class for all carousel View's. */ goog.require('tart.components.View'); goog.provide('tart.components.Carousel.View'); /** * @extends {tart.components.View} * @constructor */ tart.components.Carousel.View = function() { goog.base(this); this.template = new this.templateClass(); this.domMappings = this.template.domMappings; this.pagerItemsCache = {}; this.activeItems = null; }; goog.inherits(tart.components.Carousel.View, tart.components.View); /** * */ tart.components.Carousel.View.prototype.templateClass = tart.components.Carousel.Template; /** * Build carousel items from item array * * @param {Array.} itemArray carousel data array. */ tart.components.Carousel.View.prototype.buildCarouselItems = function(itemArray) { goog.dom.classlist.remove(this.getDOM(), 'loading'); var carouselItems = this.template.carouselItems(itemArray); this.get(this.domMappings.ITEMS)[0].innerHTML = ''; goog.dom.appendChild(this.get(this.domMappings.ITEMS)[0], carouselItems); this.itemsAppended(carouselItems); this.activeItems = carouselItems; }; /** * Render method which has to be overriden * * @return {string} markup. */ tart.components.Carousel.View.prototype.render = function() { return this.template.base(); }; /** * get all carousel items' DOM reference * * @return {Object} DOM refernce. * @protected */ tart.components.Carousel.View.prototype.getAllCarouselItemsDomReference = function() { var carouselItems = this.get(this.domMappings.ITEMS)[0]; return goog.dom.getChildren(carouselItems); }; /** * Animate carousel with given rule * * @param {string} direction 'next' or 'previous' TODO: this should be enumarated. * @param {Array.} diff items to be inserted. * @param {number} moveCount count of move. */ tart.components.Carousel.View.prototype.move = function(direction, diff, moveCount) { var that = this; if (diff.length > 0) { var carouselItems = $(that.get(that.domMappings.ITEMS)); //prevent glitch on rapid movements carouselItems.stop(true, true); var marginLeft; var moveWidth = this.template.properties.CAROUSEL_WIDTH; var allCarouselItems = $(that.getAllCarouselItemsDomReference()); var markup = $(this.template.carouselItems(diff)); this.activeItems = markup; for (var i = 0; i < markup.length; i++) { for (var j = 0; j < allCarouselItems.length; j++) { if (markup[i] === allCarouselItems[j]) allCarouselItems = allCarouselItems.not(markup[i]); } } if (direction == 'next') { carouselItems.append(markup); this.itemsAppended(markup); marginLeft = '-=' + moveWidth + 'px'; } else { carouselItems.prepend(markup); this.itemsAppended(markup); carouselItems.css('margin-left', (-1 * moveWidth) + 'px'); marginLeft = '+=' + moveWidth + 'px'; } carouselItems.animate({ 'margin-left': marginLeft }, 500, '', function(){ allCarouselItems.detach(); carouselItems.css('margin-left', '0px'); }); } }; tart.components.Carousel.View.prototype.itemsAppended = function(items) { }; /** * Method to hide previous button. */ tart.components.Carousel.View.prototype.hidePrev = function() { goog.style.showElement(this.get(this.domMappings.PREV)[0], false); }; /** * * @param {tart.base.plugin.Pager} pager to build pager. */ tart.components.Carousel.View.prototype.buildPager = function(pager) { var that = this; var pagerElement = that.get(that.domMappings.PAGER)[0]; var totalPage = pager.getTotalPage(); var navigation = that.get(that.domMappings.NAVIGATION)[0]; if (totalPage > 1) { //show pager if only totalPage > 1 if (pagerElement.length > 0) { var pagerItems = pagerElement.querySelectorAll(that.domMappings.PAGER_ITEMS)[0]; //for each pager create pager button and attach event for (var i = 1; i <= totalPage; i++) { (function(i) { var selected = (i == 1); var pagerItem = tart.dom.createElement(that.template.pagerItem(i, selected))[0]; goog.events.listen(pagerItem, goog.events.EventType.CLICK, function(){ pager.setCurrentPage(i); }); goog.dom.appendChild(pagerItems, pagerItem); that.pagerItemsCache[i] = pagerItem; })(i); } goog.style.showElement(pagerElement, true); } goog.style.showElement(navigation, true); if (!pager.hasPrev()) that.hidePrev(); } else { //hide pager if totalPage < 2 pagerElement.hide(); navigation.hide(); } }; /** * * @param {number} pageNum number of selected page. */ tart.components.Carousel.View.prototype.setPageSelected = function(pageNum) { var that = this; var pager = that.get(that.domMappings.PAGER)[0]; var pagerItems = pager.querySelector(that.domMappings.PAGER_ITEMS).querySelectorAll(that.domMappings.PAGER_ITEM); goog.array.forEach(pagerItems, function(pagerItem) { goog.dom.classlist.remove(pagerItem, 'selected'); }); that.pagerItemsCache[pageNum] && goog.dom.classlist.add(that.pagerItemsCache[pageNum], 'selected'); }; /** * * @param {boolean} hasNext Whether there is a previous page. * @param {boolean} hasPrev Whether there is a next page. */ tart.components.Carousel.View.prototype.handleNavigationButtons = function(hasNext, hasPrev) { var pagerNext = this.get(this.domMappings.NEXT)[0]; var pagerPrev = this.get(this.domMappings.PREV)[0]; goog.style.showElement(pagerNext, true); goog.style.showElement(pagerPrev, true); if (!hasNext) goog.style.showElement(pagerNext, false); if (!hasPrev) goog.style.showElement(pagerPrev, false); }; /** * No results handler * */ tart.components.Carousel.View.prototype.noResults = function() { goog.dom.classlist.remove(this.getDOM(), 'loading'); var carouselText = this.template.noResults(); this.get(this.domMappings.ITEMS)[0].innerHTML = carouselText; this.itemsAppended(carouselText); this.activeItems = carouselText; }; ================================================ FILE: tart/components/Carousel/Widget.js ================================================ // Copyright 2012 Tart. All Rights Reserved. // // @author Firat Yalavuz firat.yalavuz@tart.com.tr /** * @fileoverview tart.components.Carousel.Widget is a base for all carousel widgets. */ goog.provide('tart.components.Carousel.Widget'); goog.require('tart.components.Carousel.Controller'); goog.require('tart.components.Widget'); /** * @constructor * @extends {tart.components.Widget} */ tart.components.Carousel.Widget = function() { this.controller = new this.controllerClass(); goog.base(this); this.init(); }; goog.inherits(tart.components.Carousel.Widget, tart.components.Widget); /** * */ tart.components.Carousel.Widget.prototype.controllerClass = tart.components.Carousel.Controller; tart.components.Carousel.Widget.prototype.init = function() { this.controller.model.getItems(true); }; ================================================ FILE: tart/components/Controller.js ================================================ // Copyright 2011 Tart. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview tart.components.Controller is a base class for all components Controller's. * * Example usage: * * var ViewClass = function () { * goog.base(this); * }; * goog.inherits(ViewClass, tart.components.View); * * ViewClass.prototype.render = function () { * return "

Foo

"; * }; * var view = new ViewClass(); * * var ModelClass = function () { * goog.base(this); * }; * goog.inherits(ModelClass, tart.base.Model); * * * var model = new ModelClass(); * * var ControllerClass = function () { * goog.base(this); * }; * goog.inherits(ControllerClass, tart.components.Controller); * * var controller = new ControllerClass(model, view); * var dom = controller.buildDOM(); */ goog.require('tart.base.Model'); goog.require('tart.components.View'); goog.provide('tart.components.Controller'); /** * Base controller * * @param {tart.base.Model=} opt_model Data model. * @param {tart.components.View=} opt_view View object. * @constructor */ tart.components.Controller = function(opt_model, opt_view) { this.model = opt_model || new tart.base.Model(); this.view = opt_view || new tart.components.View(); }; /** * Build DOM from view * * @return {Element} generated DOM of attached View object. */ tart.components.Controller.prototype.buildDOM = function() { var dom = this.view.render(); // TODO : render should always be string so we should remove string check. if(goog.isString(dom)) dom = /** @type {Element} */(tart.dom.createElement(dom)); this.view.setDOM(/** @type {Element} */(dom)); return /** @type {Element} */ (dom); }; /** * Get DOM generated by view and attached by controller * * @return {Element} DOM reference. */ tart.components.Controller.prototype.getDOM = function() { return this.view.getDOM(); }; ================================================ FILE: tart/components/RemoteModel.js ================================================ // Copyright 2012 Tart. All Rights Reserved. // // @author Firat Yalavuz firat.yalavuz@tart.com.tr /** * @fileoverview tart.components.RemoteModel RemoteModel which gets data from Xhr. */ goog.provide('tart.components.models.RemoteModel'); goog.require('tart.base.Model'); goog.require('tart.dataProxy.Xhr'); /** * Remote Model * * @extends {tart.base.Model} * @constructor */ tart.components.models.RemoteModel = function() { goog.base(this); this.params.set('method_', 'post'); this.params.set('url_', this.url); this.totalCount = 0; this.proxy = new this.proxyClass(); }; goog.inherits(tart.components.models.RemoteModel, tart.base.Model); /** * RemoteModel's entity class. Default value is undefined. */ tart.components.models.RemoteModel.prototype.entityClass = undefined; tart.components.models.RemoteModel.prototype.proxyClass = tart.dataProxy.Xhr; /** * RemoteModel's request url. Default value is undefined. */ tart.components.models.RemoteModel.prototype.url = undefined; /** * Load data and map data to entity. * @param {Function=} opt_callback method to call after load. */ tart.components.models.RemoteModel.prototype.load = function(opt_callback) { var that = this; that.proxy.setParams(this.params); that.proxy.fetch(function(data) { that.mapDataToEntities(data); if (opt_callback) { opt_callback.call(this, that.getItems()); } }); }; /** * map data to entity class. * @param {Object} xhrData data returned from xhr request. * @protected */ tart.components.models.RemoteModel.prototype.mapDataToEntities = function(xhrData) { var items = [], collection = xhrData['data']; for (var i = 0, l = collection.length; i < l; i++) { items.push(new this.entityClass(collection[i])); } this.totalCount = items.length; this.setItems(items); }; ================================================ FILE: tart/components/ThumbnailedCarousel/Model.js ================================================ // Copyright 2012 Tart. All Rights Reserved. // // @author Firat Yalavuz firat.yalavuz@tart.com.tr goog.require('tart.components.Carousel.Model'); goog.provide('tart.components.ThumbnailedCarousel.Model'); /** * @extends {tart.components.Carousel.Model} * @constructor */ tart.components.ThumbnailedCarousel.Model = function() { goog.base(this); }; goog.inherits(tart.components.ThumbnailedCarousel.Model, tart.components.Carousel.Model); /** * @param {Array} activeItem Active carousel items. */ tart.components.ThumbnailedCarousel.Model.prototype.setActiveItem = function(activeItem) { this.activeItem = activeItem; this.dispatchEvent({ type: this.EventTypes.ACTIVE_ITEM, activeItem: activeItem }); }; tart.components.ThumbnailedCarousel.Model.prototype.EventTypes = { ACTIVE_ITEM: 'activeItem' }; ================================================ FILE: tart/components/ThumbnailedCarousel/SpotController.js ================================================ // Copyright 2012 Tart. All Rights Reserved. // // @author Firat Yalavuz firat.yalavuz@tart.com.tr goog.require('tart.CircularPagination'); goog.require('tart.components.Carousel.Controller'); goog.require('tart.components.ThumbnailedCarousel.Model'); goog.require('tart.components.ThumbnailedCarousel.ThumbnailsController'); goog.require('tart.components.ThumbnailedCarousel.ThumbnailsTemplate'); goog.require('tart.components.ThumbnailedCarousel.SpotView'); goog.provide('tart.components.ThumbnailedCarousel.SpotController'); /** * @constructor * @extends {tart.components.Carousel.Controller} */ tart.components.ThumbnailedCarousel.SpotController = function() { this.pagination = new tart.CircularPagination(); goog.base(this); }; goog.inherits(tart.components.ThumbnailedCarousel.SpotController, tart.components.Carousel.Controller); /** * @override */ tart.components.ThumbnailedCarousel.SpotController.prototype.paginationClass = tart.CircularPagination; /** * @override */ tart.components.ThumbnailedCarousel.SpotController.prototype.itemCount = 1; /** * @override */ tart.components.ThumbnailedCarousel.SpotController.prototype.viewClass = tart.components.ThumbnailedCarousel.SpotView; /** * @override */ tart.components.ThumbnailedCarousel.SpotController.prototype.modelClass = tart.components.ThumbnailedCarousel.Model; /** * Thumbnailed Carousel's thumbnails template class */ tart.components.ThumbnailedCarousel.SpotController.prototype.thumbnailsTemplateClass = tart.components.ThumbnailedCarousel.ThumbnailsTemplate; /** * Thumbnailed Carousel's thumbnails controller class */ tart.components.ThumbnailedCarousel.SpotController.prototype.thumbnailsControllerClass = tart.components.ThumbnailedCarousel.ThumbnailsController; /** * @override */ tart.components.ThumbnailedCarousel.SpotController.prototype.buildCarouselAction = function(visibleItems, totalItemCount) { goog.base(this, 'buildCarouselAction', visibleItems, totalItemCount); this.thumbnailsController = new this.thumbnailsControllerClass(this.model); var $thumbnails = this.thumbnailsController.getDOM(); this.view.appendThumbnails($thumbnails); }; /** * @override */ tart.components.ThumbnailedCarousel.SpotController.prototype.bindEvents = function() { goog.base(this, 'bindEvents'); var that = this; goog.events.listen(this.modelPager, tart.Pagination.EventTypes.PAGE_CHANGED, function(e) { that.model.setActiveItem(e.newValue); }); goog.events.listen(this.model, this.model.EventTypes.ACTIVE_ITEM, function(e) { that.modelPager.setCurrentPage(e.activeItem); }); }; ================================================ FILE: tart/components/ThumbnailedCarousel/SpotTemplate.js ================================================ // Copyright 2012 Tart. All Rights Reserved. // // @author Firat Yalavuz firat.yalavuz@tart.com.tr goog.require('tart.components.Carousel.Template'); goog.provide('tart.components.ThumbnailedCarousel.SpotTemplate'); /** * @extends {tart.components.Carousel.Template} * @constructor */ tart.components.ThumbnailedCarousel.SpotTemplate = function() { goog.base(this); this.domMappings.THUMBNAILS_CONTAINER = '.thumbs'; }; goog.inherits(tart.components.ThumbnailedCarousel.SpotTemplate, tart.components.Carousel.Template); /** * Base function to create dom. * * @return {string} base markup of spot. */ tart.components.ThumbnailedCarousel.SpotTemplate.prototype.base = function() { var markup = '