From 451a3400b76511393c62a444f588a4ed15f4a549 Mon Sep 17 00:00:00 2001 From: Michael Lando Date: Sun, 19 Feb 2017 10:28:42 +0200 Subject: Initial OpenECOMP SDC commit Change-Id: I0924d5a6ae9cdc161ae17c68d3689a30d10f407b Signed-off-by: Michael Lando --- catalog-ui/app/third-party/PunchOutRegistry.js | 98 +++++++++ .../app/third-party/ng-infinite-scroll/.npmignore | 3 + .../app/third-party/ng-infinite-scroll/.travis.yml | 12 ++ .../ng-infinite-scroll/Gruntfile.coffee | 110 ++++++++++ .../app/third-party/ng-infinite-scroll/LICENSE | 22 ++ .../app/third-party/ng-infinite-scroll/README.md | 71 +++++++ .../app/third-party/ng-infinite-scroll/bower.json | 16 ++ .../ng-infinite-scroll/build/ng-infinite-scroll.js | 209 +++++++++++++++++++ .../build/ng-infinite-scroll.min.js | 22 ++ .../third-party/ng-infinite-scroll/package.json | 80 ++++++++ .../ng-infinite-scroll/src/infinite-scroll.coffee | 209 +++++++++++++++++++ .../test/protractor-local.conf.js | 27 +++ .../test/protractor-shared.conf.js | 26 +++ .../test/protractor-travis.conf.js | 32 +++ .../test/spec/ng-infinite-scroll.spec.coffee | 221 +++++++++++++++++++++ 15 files changed, 1158 insertions(+) create mode 100644 catalog-ui/app/third-party/PunchOutRegistry.js create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/.npmignore create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/.travis.yml create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/Gruntfile.coffee create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/LICENSE create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/README.md create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/bower.json create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.js create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.min.js create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/package.json create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/src/infinite-scroll.coffee create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-local.conf.js create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-shared.conf.js create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-travis.conf.js create mode 100644 catalog-ui/app/third-party/ng-infinite-scroll/test/spec/ng-infinite-scroll.spec.coffee (limited to 'catalog-ui/app/third-party') diff --git a/catalog-ui/app/third-party/PunchOutRegistry.js b/catalog-ui/app/third-party/PunchOutRegistry.js new file mode 100644 index 0000000000..bc93453dc1 --- /dev/null +++ b/catalog-ui/app/third-party/PunchOutRegistry.js @@ -0,0 +1,98 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +(function(window) { + "use strict"; + + if (window.PunchOutRegistry) { + return; + } + + var queuedFactoryRequests = new Map(); + var factoryPromises = new Map(); + var instancePromises = new Map(); + + function registerFactory(name, factory) { + if (factoryPromises.has(name) && !queuedFactoryRequests.has(name)) { + console.error("PunchOut \"" + name + "\" has been already registered"); + return; + } + if (queuedFactoryRequests.has(name)) { + var factoryRequest = queuedFactoryRequests.get(name); + factoryRequest(factory); + queuedFactoryRequests.delete(name); + } else { + factoryPromises.set(name, Promise.resolve(factory)); + } + } + + function getFactoryPromise(name) { + var factoryPromise = factoryPromises.get(name); + if (!factoryPromise) { + factoryPromise = new Promise(function (resolveFactory) { + queuedFactoryRequests.set(name, resolveFactory); + }); + factoryPromises.set(name, factoryPromise); + } + return factoryPromise; + } + + function getInstancePromise(name, element) { + var factoryPromise; + var instancePromise = instancePromises.get(element); + if (!instancePromise) { + instancePromise = getFactoryPromise(name).then(function(factory) { + return factory(); + }); + instancePromises.set(element, instancePromise); + } + return instancePromise; + } + + function renderPunchOut(params, element) { + var name = params.name; + var options = params.options || {}; + var onEvent = params.onEvent || function () {}; + + getInstancePromise(name, element).then(function (punchOut) { + punchOut.render({options: options, onEvent: onEvent}, element); + }); + } + + function unmountPunchOut(element) { + if (!instancePromises.has(element)) { + console.error("There is no PunchOut in element", element); + return; + } + instancePromises.get(element).then(function(punchOut) { + punchOut.unmount(element); + }); + instancePromises.delete(element); + } + + var PunchOutRegistry = Object.freeze({ + register: registerFactory, + render: renderPunchOut, + unmount: unmountPunchOut + }); + + window.PunchOutRegistry = PunchOutRegistry; + +})(this); diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/.npmignore b/catalog-ui/app/third-party/ng-infinite-scroll/.npmignore new file mode 100644 index 0000000000..dcbd092327 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/.npmignore @@ -0,0 +1,3 @@ +compile/* +node_modules +.tmp diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/.travis.yml b/catalog-ui/app/third-party/ng-infinite-scroll/.travis.yml new file mode 100644 index 0000000000..ee95ccb343 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/.travis.yml @@ -0,0 +1,12 @@ +language: node_js +node_js: + - 0.10 + +install: + - npm install + +before_script: + - npm install -g grunt-cli + +script: + - grunt test:protractor-travis diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/Gruntfile.coffee b/catalog-ui/app/third-party/ng-infinite-scroll/Gruntfile.coffee new file mode 100644 index 0000000000..a9b9405731 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/Gruntfile.coffee @@ -0,0 +1,110 @@ +module.exports = (grunt) -> + grunt.loadNpmTasks 'grunt-coffeelint' + grunt.loadNpmTasks 'grunt-contrib-clean' + grunt.loadNpmTasks 'grunt-contrib-coffee' + grunt.loadNpmTasks 'grunt-contrib-concat' + grunt.loadNpmTasks 'grunt-contrib-connect' + grunt.loadNpmTasks 'grunt-contrib-uglify' + grunt.loadNpmTasks 'grunt-protractor-runner' + + sauceUser = 'pomerantsevp' + sauceKey = '497ab04e-f31b-4a7b-9b18-ae3fbe023222' + + grunt.initConfig + pkg: grunt.file.readJSON 'package.json' + meta: + banner: '/* <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> */\n' + coffeelint: + src: 'src/**/*.coffee' + options: + max_line_length: + level: 'ignore' + line_endings: + value: 'unix' + level: 'error' + no_stand_alone_at: + level: 'error' + clean: + options: + force: true + build: ["compile/**", "build/**"] + coffee: + compile: + files: [ + { + expand: true + cwd: 'src/' + src: '**/*.coffee' + dest: 'compile/' + ext: '.js' + } + ], + options: + bare: true + concat: + options: + banner: '<%= meta.banner %>' + dist: + src: 'compile/**/*.js' + dest: 'build/ng-infinite-scroll.js' + uglify: + options: + banner: '<%= meta.banner %>' + dist: + src: ['build/ng-infinite-scroll.js'] + dest: 'build/ng-infinite-scroll.min.js' + connect: + testserver: + options: + port: 8000 + hostname: '0.0.0.0' + middleware: (connect, options) -> + base = if Array.isArray(options.base) then options.base[options.base.length - 1] else options.base + [connect.static(base)] + protractor: + local: + options: + configFile: 'test/protractor-local.conf.js' + args: + params: + testThrottleValue: 500 + travis: + options: + configFile: 'test/protractor-travis.conf.js' + args: + params: + # When using Sauce Connect, we should use a large timeout + # since everything is generally much slower than when testing locally. + testThrottleValue: 10000 + sauceUser: sauceUser + sauceKey: sauceKey + + grunt.registerTask 'webdriver', () -> + done = this.async() + p = require('child_process').spawn('node', ['node_modules/protractor/bin/webdriver-manager', 'update']) + p.stdout.pipe(process.stdout) + p.stderr.pipe(process.stderr) + p.on 'exit', (code) -> + if code isnt 0 then grunt.fail.warn('Webdriver failed to update') + done() + + grunt.registerTask 'sauce-connect', () -> + done = this.async() + require('sauce-connect-launcher')({username: sauceUser, accessKey: sauceKey}, (err, sauceConnectProcess) -> + if err then console.error(err.message) + else done() + ) + + grunt.registerTask 'default', ['coffeelint', 'clean', 'coffee', 'concat', 'uglify'] + grunt.registerTask 'test:protractor-local', [ + 'default', + 'webdriver', + 'connect:testserver', + 'protractor:local' + ] + + grunt.registerTask 'test:protractor-travis', [ + 'connect:testserver', + 'sauce-connect', + 'protractor:travis' + ] diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/LICENSE b/catalog-ui/app/third-party/ng-infinite-scroll/LICENSE new file mode 100644 index 0000000000..44ae2bfc40 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012 Michelle Tilley + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/README.md b/catalog-ui/app/third-party/ng-infinite-scroll/README.md new file mode 100644 index 0000000000..aa8d8630e2 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/README.md @@ -0,0 +1,71 @@ +[**Maintainer help needed**: I'm looking for fellows that are willing to help me maintain and improve this project.](https://github.com/sroze/ngInfiniteScroll/issues/267) + +--- + +![logo](http://sroze.github.com/ngInfiniteScroll/images/logo-resized.png) + +[![Build Status](https://travis-ci.org/sroze/ngInfiniteScroll.png?branch=master)](https://travis-ci.org/sroze/ngInfiniteScroll) + +ngInfiniteScroll is a directive for [AngularJS](http://angularjs.org/) to evaluate an expression when the bottom of the directive's element approaches the bottom of the browser window, which can be used to implement infinite scrolling. + +Demos +----- + +Check out the running demos [at the ngInfiniteScroll web site](http://sroze.github.com/ngInfiniteScroll/demos.html). + +Version Numbers +--------------- + +ngInfinite Scroll follows [semantic versioning](http://semver.org/) and uses the following versioning scheme: + + * Versions starting with 0 (e.g. 0.1.0, 0.2.0, etc.) are for initial development, and the API is not stable + * Versions with an even minor version (1.0.0, 1.4.0, 2.2.0, etc.) are stable releases + * Versions with an odd minor version (1.1.0, 1.3.0, 2.1.0, etc.) are development releases + +The [download page](http://sroze.github.com/ngInfiniteScroll/#download) allows you to pick among various versions and specify which releases are stable (not including pre-release builds). + +Getting Started +--------------- + + * Download ngInfiniteScroll from [the download page on the ngInfiniteScroll web site](http://sroze.github.com/ngInfiniteScroll/#download) or install it with: + * [Bower](http://bower.io/) via `bower install ngInfiniteScroll` + * [NPM](https://www.npmjs.com) via `npm install --save ng-infinite-scroll` + * [Nuget](https://www.nuget.org) via `PM> Install-Package ng-infinite-scroll` + * Include the script tag on your page after the AngularJS script tag (ngInfiniteScroll *doesn't* require jQuery) + + + + + * Ensure that your application module specifies `infinite-scroll` as a dependency: + + angular.module('myApplication', ['infinite-scroll']); + + * Use the directive by specifying an `infinite-scroll` attribute on an element. + +
+ +Note that neither the module nor the directive use the `ng` prefix, as that prefix is reserved for the core Angular module. + +Detailed Documentation +---------------------- + +ngInfiniteScroll accepts several attributes to customize the behavior of the directive; detailed instructions can be found [on the ngInfiniteScroll web site](http://sroze.github.com/ngInfiniteScroll/documentation.html). + +Ports +----- + +If you use [AngularDart](https://github.com/angular/angular.dart), Juha Komulainen has [a port of the project](http://pub.dartlang.org/packages/ng_infinite_scroll) you can use. + +License +------- + +ngInfiniteScroll is licensed under the MIT license. See the LICENSE file for more details. + +Testing +------- + +ngInfiniteScroll uses Protractor for testing. Note that you will need to have Chrome browser, and the `grunt-cli` npm package installed globally if you wish to use grunt (`npm install -g grunt-cli`). Then, install the dependencies with `npm install`. + +* `grunt test:protractor-local` - run tests + +Thank you very much @pomerantsev for your work on these Protractor tests. diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/bower.json b/catalog-ui/app/third-party/ng-infinite-scroll/bower.json new file mode 100644 index 0000000000..1171c45190 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/bower.json @@ -0,0 +1,16 @@ +{ + "name": "ngInfiniteScroll", + "main": "build/ng-infinite-scroll.js", + "ignore": [ + "**/.*", + "_*", + "node_modules", + "compile", + "test", + "Gruntfile.coffee", + ".*" + ], + "dependencies": { + "angular": ">=1.2.0" + } +} diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.js b/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.js new file mode 100644 index 0000000000..0585004832 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.js @@ -0,0 +1,209 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +/* ng-infinite-scroll - v1.3.0 - 2016-06-30 */ +angular.module('infinite-scroll', []).value('THROTTLE_MILLISECONDS', null).directive('infiniteScroll', [ + '$rootScope', '$window', '$interval', 'THROTTLE_MILLISECONDS', function($rootScope, $window, $interval, THROTTLE_MILLISECONDS) { + return { + scope: { + infiniteScroll: '&', + infiniteScrollContainer: '=', + infiniteScrollDistance: '=', + infiniteScrollDisabled: '=', + infiniteScrollUseDocumentBottom: '=', + infiniteScrollListenForEvent: '@' + }, + link: function(scope, elem, attrs) { + var changeContainer, checkInterval, checkWhenEnabled, container, handleInfiniteScrollContainer, handleInfiniteScrollDisabled, handleInfiniteScrollDistance, handleInfiniteScrollUseDocumentBottom, handler, height, immediateCheck, offsetTop, pageYOffset, scrollDistance, scrollEnabled, throttle, unregisterEventListener, useDocumentBottom, windowElement; + windowElement = angular.element($window); + scrollDistance = null; + scrollEnabled = null; + checkWhenEnabled = null; + container = null; + immediateCheck = true; + useDocumentBottom = false; + unregisterEventListener = null; + checkInterval = false; + height = function(elem) { + elem = elem[0] || elem; + if (isNaN(elem.offsetHeight)) { + return elem.document.documentElement.clientHeight; + } else { + return elem.offsetHeight; + } + }; + offsetTop = function(elem) { + if (!elem[0].getBoundingClientRect || elem.css('none')) { + return; + } + return elem[0].getBoundingClientRect().top + pageYOffset(elem); + }; + pageYOffset = function(elem) { + elem = elem[0] || elem; + if (isNaN(window.pageYOffset)) { + return elem.document.documentElement.scrollTop; + } else { + return elem.ownerDocument.defaultView.pageYOffset; + } + }; + handler = function() { + var containerBottom, containerTopOffset, elementBottom, remaining, shouldScroll; + if (container === windowElement) { + containerBottom = height(container) + pageYOffset(container[0].document.documentElement); + elementBottom = offsetTop(elem) + height(elem); + } else { + containerBottom = height(container); + containerTopOffset = 0; + if (offsetTop(container) !== void 0) { + containerTopOffset = offsetTop(container); + } + elementBottom = offsetTop(elem) - containerTopOffset + height(elem); + } + if (useDocumentBottom) { + elementBottom = height((elem[0].ownerDocument || elem[0].document).documentElement); + } + remaining = elementBottom - containerBottom; + shouldScroll = remaining <= height(container) * scrollDistance + 1; + if (shouldScroll) { + checkWhenEnabled = true; + if (scrollEnabled) { + if (scope.$$phase || $rootScope.$$phase) { + return scope.infiniteScroll(); + } else { + return scope.$apply(scope.infiniteScroll); + } + } + } else { + if (checkInterval) { + $interval.cancel(checkInterval); + } + return checkWhenEnabled = false; + } + }; + throttle = function(func, wait) { + var later, previous, timeout; + timeout = null; + previous = 0; + later = function() { + previous = new Date().getTime(); + $interval.cancel(timeout); + timeout = null; + return func.call(); + }; + return function() { + var now, remaining; + now = new Date().getTime(); + remaining = wait - (now - previous); + if (remaining <= 0) { + $interval.cancel(timeout); + timeout = null; + previous = now; + return func.call(); + } else { + if (!timeout) { + return timeout = $interval(later, remaining, 1); + } + } + }; + }; + if (THROTTLE_MILLISECONDS != null) { + handler = throttle(handler, THROTTLE_MILLISECONDS); + } + scope.$on('$destroy', function() { + container.unbind('scroll', handler); + if (unregisterEventListener != null) { + unregisterEventListener(); + unregisterEventListener = null; + } + if (checkInterval) { + return $interval.cancel(checkInterval); + } + }); + handleInfiniteScrollDistance = function(v) { + return scrollDistance = parseFloat(v) || 0; + }; + scope.$watch('infiniteScrollDistance', handleInfiniteScrollDistance); + handleInfiniteScrollDistance(scope.infiniteScrollDistance); + handleInfiniteScrollDisabled = function(v) { + scrollEnabled = !v; + if (scrollEnabled && checkWhenEnabled) { + checkWhenEnabled = false; + return handler(); + } + }; + scope.$watch('infiniteScrollDisabled', handleInfiniteScrollDisabled); + handleInfiniteScrollDisabled(scope.infiniteScrollDisabled); + handleInfiniteScrollUseDocumentBottom = function(v) { + return useDocumentBottom = v; + }; + scope.$watch('infiniteScrollUseDocumentBottom', handleInfiniteScrollUseDocumentBottom); + handleInfiniteScrollUseDocumentBottom(scope.infiniteScrollUseDocumentBottom); + changeContainer = function(newContainer) { + if (container != null) { + container.unbind('scroll', handler); + } + container = newContainer; + if (newContainer != null) { + return container.bind('scroll', handler); + } + }; + changeContainer(windowElement); + if (scope.infiniteScrollListenForEvent) { + unregisterEventListener = $rootScope.$on(scope.infiniteScrollListenForEvent, handler); + } + handleInfiniteScrollContainer = function(newContainer) { + if ((newContainer == null) || newContainer.length === 0) { + return; + } + if (newContainer.nodeType && newContainer.nodeType === 1) { + newContainer = angular.element(newContainer); + } else if (typeof newContainer.append === 'function') { + newContainer = angular.element(newContainer[newContainer.length - 1]); + } else if (typeof newContainer === 'string') { + newContainer = angular.element(document.querySelector(newContainer)); + } + if (newContainer != null) { + return changeContainer(newContainer); + } else { + throw new Error("invalid infinite-scroll-container attribute."); + } + }; + scope.$watch('infiniteScrollContainer', handleInfiniteScrollContainer); + handleInfiniteScrollContainer(scope.infiniteScrollContainer || []); + if (attrs.infiniteScrollParent != null) { + changeContainer(angular.element(elem.parent())); + } + if (attrs.infiniteScrollImmediateCheck != null) { + immediateCheck = scope.$eval(attrs.infiniteScrollImmediateCheck); + } + return checkInterval = $interval((function() { + if (immediateCheck) { + handler(); + } + return $interval.cancel(checkInterval); + })); + } + }; + } +]); + +if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports) { + module.exports = 'infinite-scroll'; +} diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.min.js b/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.min.js new file mode 100644 index 0000000000..e2a3036422 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.min.js @@ -0,0 +1,22 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +/* ng-infinite-scroll - v1.3.0 - 2016-06-30 */ +angular.module("infinite-scroll",[]).value("THROTTLE_MILLISECONDS",null).directive("infiniteScroll",["$rootScope","$window","$interval","THROTTLE_MILLISECONDS",function(a,b,c,d){return{scope:{infiniteScroll:"&",infiniteScrollContainer:"=",infiniteScrollDistance:"=",infiniteScrollDisabled:"=",infiniteScrollUseDocumentBottom:"=",infiniteScrollListenForEvent:"@"},link:function(e,f,g){var h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;return z=angular.element(b),u=null,v=null,j=null,k=null,r=!0,y=!1,x=null,i=!1,q=function(a){return a=a[0]||a,isNaN(a.offsetHeight)?a.document.documentElement.clientHeight:a.offsetHeight},s=function(a){if(a[0].getBoundingClientRect&&!a.css("none"))return a[0].getBoundingClientRect().top+t(a)},t=function(a){return a=a[0]||a,isNaN(window.pageYOffset)?a.document.documentElement.scrollTop:a.ownerDocument.defaultView.pageYOffset},p=function(){var b,d,g,h,l;return k===z?(b=q(k)+t(k[0].document.documentElement),g=s(f)+q(f)):(b=q(k),d=0,void 0!==s(k)&&(d=s(k)),g=s(f)-d+q(f)),y&&(g=q((f[0].ownerDocument||f[0].document).documentElement)),h=g-b,l=h<=q(k)*u+1,l?(j=!0,v?e.$$phase||a.$$phase?e.infiniteScroll():e.$apply(e.infiniteScroll):void 0):(i&&c.cancel(i),j=!1)},w=function(a,b){var d,e,f;return f=null,e=0,d=function(){return e=(new Date).getTime(),c.cancel(f),f=null,a.call()},function(){var g,h;return g=(new Date).getTime(),h=b-(g-e),h<=0?(c.cancel(f),f=null,e=g,a.call()):f?void 0:f=c(d,h,1)}},null!=d&&(p=w(p,d)),e.$on("$destroy",function(){if(k.unbind("scroll",p),null!=x&&(x(),x=null),i)return c.cancel(i)}),n=function(a){return u=parseFloat(a)||0},e.$watch("infiniteScrollDistance",n),n(e.infiniteScrollDistance),m=function(a){if(v=!a,v&&j)return j=!1,p()},e.$watch("infiniteScrollDisabled",m),m(e.infiniteScrollDisabled),o=function(a){return y=a},e.$watch("infiniteScrollUseDocumentBottom",o),o(e.infiniteScrollUseDocumentBottom),h=function(a){if(null!=k&&k.unbind("scroll",p),k=a,null!=a)return k.bind("scroll",p)},h(z),e.infiniteScrollListenForEvent&&(x=a.$on(e.infiniteScrollListenForEvent,p)),l=function(a){if(null!=a&&0!==a.length){if(a.nodeType&&1===a.nodeType?a=angular.element(a):"function"==typeof a.append?a=angular.element(a[a.length-1]):"string"==typeof a&&(a=angular.element(document.querySelector(a))),null!=a)return h(a);throw new Error("invalid infinite-scroll-container attribute.")}},e.$watch("infiniteScrollContainer",l),l(e.infiniteScrollContainer||[]),null!=g.infiniteScrollParent&&h(angular.element(f.parent())),null!=g.infiniteScrollImmediateCheck&&(r=e.$eval(g.infiniteScrollImmediateCheck)),i=c(function(){return r&&p(),c.cancel(i)})}}}]),"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="infinite-scroll"); diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/package.json b/catalog-ui/app/third-party/ng-infinite-scroll/package.json new file mode 100644 index 0000000000..8859faecdc --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/package.json @@ -0,0 +1,80 @@ +{ + "name": "ng-infinite-scroll", + "version": "1.3.0", + "description": "Infinite scrolling for AngularJS", + "repository": { + "type": "git", + "url": "git://github.com/sroze/ngInfiniteScroll.git" + }, + "main": "build/ng-infinite-scroll.js", + "browser": "build/ng-infinite-scroll.js", + "scripts": { + "test": "grunt test:protractor-local" + }, + "author": { + "name": "Michelle Tilley", + "email": "michelle@michelletilley.net", + "url": "http://michelletilley.net" + }, + "contributors": [ + { + "name": "Samuel ROZE", + "email": "samuel.roze@gmail.com", + "url": "http://sroze.io" + } + ], + "license": "MIT", + "devDependencies": { + "coffee-script": "~1.8.0", + "grunt": "~0.4.5", + "grunt-coffeelint": "~0.0.13", + "grunt-contrib-clean": "~0.6.0", + "grunt-contrib-coffee": "~0.12.0", + "grunt-contrib-concat": "~0.5.0", + "grunt-contrib-connect": "0.8.0", + "grunt-contrib-uglify": "~0.6.0", + "grunt-protractor-runner": "1.1.4", + "mkdirp": "0.5.0", + "protractor": "1.4.0", + "sauce-connect-launcher": "0.9.0" + }, + "gitHead": "6fbf4b41947f9a3023b4aba0e613231950ccc4a1", + "bugs": { + "url": "https://github.com/sroze/ngInfiniteScroll/issues" + }, + "homepage": "https://github.com/sroze/ngInfiniteScroll#readme", + "_id": "ng-infinite-scroll@1.3.0", + "_shasum": "c2e98d8fd134b0525a4d2cf58c95d9b583755112", + "_from": "ng-infinite-scroll@>=1.3.0 <2.0.0", + "_npmVersion": "3.9.5", + "_nodeVersion": "6.2.2", + "_npmUser": { + "name": "graingert", + "email": "tagrain@gmail.com" + }, + "dist": { + "shasum": "c2e98d8fd134b0525a4d2cf58c95d9b583755112", + "tarball": "https://registry.npmjs.org/ng-infinite-scroll/-/ng-infinite-scroll-1.3.0.tgz" + }, + "maintainers": [ + { + "name": "binarymuse", + "email": "michelle@michelletilley.net" + }, + { + "name": "graingert", + "email": "tagrain@gmail.com" + }, + { + "name": "sroze", + "email": "samuel.roze@gmail.com" + } + ], + "_npmOperationalInternal": { + "host": "packages-12-west.internal.npmjs.com", + "tmp": "tmp/ng-infinite-scroll-1.3.0.tgz_1467302375605_0.8192659560590982" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/ng-infinite-scroll/-/ng-infinite-scroll-1.3.0.tgz", + "readme": "ERROR: No README data found!" +} diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/src/infinite-scroll.coffee b/catalog-ui/app/third-party/ng-infinite-scroll/src/infinite-scroll.coffee new file mode 100644 index 0000000000..cf2f90fc19 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/src/infinite-scroll.coffee @@ -0,0 +1,209 @@ +angular.module('infinite-scroll', []) + .value('THROTTLE_MILLISECONDS', null) + .directive 'infiniteScroll', [ + '$rootScope', '$window', '$interval', 'THROTTLE_MILLISECONDS', +($rootScope, $window, $interval, THROTTLE_MILLISECONDS) -> + scope: + infiniteScroll: '&' + infiniteScrollContainer: '=' + infiniteScrollDistance: '=' + infiniteScrollDisabled: '=' + infiniteScrollUseDocumentBottom: '=', + infiniteScrollListenForEvent: '@' + + link: (scope, elem, attrs) -> + windowElement = angular.element($window) + + scrollDistance = null + scrollEnabled = null + checkWhenEnabled = null + container = null + immediateCheck = true + useDocumentBottom = false + unregisterEventListener = null + checkInterval = false + + height = (elem) -> + elem = elem[0] or elem + + if isNaN(elem.offsetHeight) then elem.document.documentElement.clientHeight else elem.offsetHeight + + offsetTop = (elem) -> + if not elem[0].getBoundingClientRect or elem.css('none') + return + + elem[0].getBoundingClientRect().top + pageYOffset(elem) + + pageYOffset = (elem) -> + elem = elem[0] or elem + + if isNaN(window.pageYOffset) then elem.document.documentElement.scrollTop else elem.ownerDocument.defaultView.pageYOffset + + # infinite-scroll specifies a function to call when the window, + # or some other container specified by infinite-scroll-container, + # is scrolled within a certain range from the bottom of the + # document. It is recommended to use infinite-scroll-disabled + # with a boolean that is set to true when the function is + # called in order to throttle the function call. + handler = -> + if container == windowElement + containerBottom = height(container) + pageYOffset(container[0].document.documentElement) + elementBottom = offsetTop(elem) + height(elem) + else + containerBottom = height(container) + containerTopOffset = 0 + if offsetTop(container) != undefined + containerTopOffset = offsetTop(container) + elementBottom = offsetTop(elem) - containerTopOffset + height(elem) + + if(useDocumentBottom) + elementBottom = height((elem[0].ownerDocument || elem[0].document).documentElement) + + remaining = elementBottom - containerBottom + shouldScroll = remaining <= height(container) * scrollDistance + 1 + + if shouldScroll + checkWhenEnabled = true + + if scrollEnabled + if scope.$$phase || $rootScope.$$phase + scope.infiniteScroll() + else + scope.$apply(scope.infiniteScroll) + else + if checkInterval then $interval.cancel checkInterval + checkWhenEnabled = false + + # The optional THROTTLE_MILLISECONDS configuration value specifies + # a minimum time that should elapse between each call to the + # handler. N.b. the first call the handler will be run + # immediately, and the final call will always result in the + # handler being called after the `wait` period elapses. + # A slimmed down version of underscore's implementation. + throttle = (func, wait) -> + timeout = null + previous = 0 + later = -> + previous = new Date().getTime() + $interval.cancel(timeout) + timeout = null + func.call() + + return -> + now = new Date().getTime() + remaining = wait - (now - previous) + if remaining <= 0 + $interval.cancel(timeout) + timeout = null + previous = now + func.call() + else timeout = $interval(later, remaining, 1) unless timeout + + if THROTTLE_MILLISECONDS? + handler = throttle(handler, THROTTLE_MILLISECONDS) + + scope.$on '$destroy', -> + container.unbind 'scroll', handler + if unregisterEventListener? + unregisterEventListener() + unregisterEventListener = null + if checkInterval + $interval.cancel checkInterval + + # infinite-scroll-distance specifies how close to the bottom of the page + # the window is allowed to be before we trigger a new scroll. The value + # provided is multiplied by the container height; for example, to load + # more when the bottom of the page is less than 3 container heights away, + # specify a value of 3. Defaults to 0. + handleInfiniteScrollDistance = (v) -> + scrollDistance = parseFloat(v) or 0 + + scope.$watch 'infiniteScrollDistance', handleInfiniteScrollDistance + # If I don't explicitly call the handler here, tests fail. Don't know why yet. + handleInfiniteScrollDistance scope.infiniteScrollDistance + + # infinite-scroll-disabled specifies a boolean that will keep the + # infnite scroll function from being called; this is useful for + # debouncing or throttling the function call. If an infinite + # scroll is triggered but this value evaluates to true, then + # once it switches back to false the infinite scroll function + # will be triggered again. + handleInfiniteScrollDisabled = (v) -> + scrollEnabled = !v + if scrollEnabled && checkWhenEnabled + checkWhenEnabled = false + handler() + + scope.$watch 'infiniteScrollDisabled', handleInfiniteScrollDisabled + # If I don't explicitly call the handler here, tests fail. Don't know why yet. + handleInfiniteScrollDisabled scope.infiniteScrollDisabled + + # use the bottom of the document instead of the element's bottom. + # This useful when the element does not have a height due to its + # children being absolute positioned. + handleInfiniteScrollUseDocumentBottom = (v) -> + useDocumentBottom = v + + scope.$watch 'infiniteScrollUseDocumentBottom', handleInfiniteScrollUseDocumentBottom + handleInfiniteScrollUseDocumentBottom scope.infiniteScrollUseDocumentBottom + + # infinite-scroll-container sets the container which we want to be + # infinte scrolled, instead of the whole window. Must be an + # Angular or jQuery element, or, if jQuery is loaded, + # a jQuery selector as a string. + changeContainer = (newContainer) -> + if container? + container.unbind 'scroll', handler + + container = newContainer + if newContainer? + container.bind 'scroll', handler + + changeContainer windowElement + + if scope.infiniteScrollListenForEvent + unregisterEventListener = $rootScope.$on scope.infiniteScrollListenForEvent, handler + + handleInfiniteScrollContainer = (newContainer) -> + # TODO: For some reason newContainer is sometimes null instead + # of the empty array, which Angular is supposed to pass when the + # element is not defined + # (https://github.com/sroze/ngInfiniteScroll/pull/7#commitcomment-5748431). + # So I leave both checks. + if (not newContainer?) or newContainer.length == 0 + return + + if newContainer.nodeType && newContainer.nodeType == 1 + newContainer = angular.element newContainer + else if typeof newContainer.append == 'function' + newContainer = angular.element newContainer[newContainer.length - 1] + else if typeof newContainer == 'string' + newContainer = angular.element document.querySelector newContainer + + if newContainer? + changeContainer newContainer + else + throw new Error("invalid infinite-scroll-container attribute.") + + scope.$watch 'infiniteScrollContainer', handleInfiniteScrollContainer + handleInfiniteScrollContainer(scope.infiniteScrollContainer or []) + + # infinite-scroll-parent establishes this element's parent as the + # container infinitely scrolled instead of the whole window. + if attrs.infiniteScrollParent? + changeContainer angular.element elem.parent() + + # infinte-scoll-immediate-check sets whether or not run the + # expression passed on infinite-scroll for the first time when the + # directive first loads, before any actual scroll. + if attrs.infiniteScrollImmediateCheck? + immediateCheck = scope.$eval(attrs.infiniteScrollImmediateCheck) + + checkInterval = $interval (-> + if immediateCheck + handler() + $interval.cancel checkInterval + ) +] +if typeof module != "undefined" && typeof exports != "undefined" && module.exports == exports + module.exports = 'infinite-scroll' diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-local.conf.js b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-local.conf.js new file mode 100644 index 0000000000..134c723f91 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-local.conf.js @@ -0,0 +1,27 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +var config = require('./protractor-shared.conf').config; + +config.multiCapabilities = [ + { browserName: 'chrome' } +]; + +exports.config = config; diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-shared.conf.js b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-shared.conf.js new file mode 100644 index 0000000000..d1e567d7b4 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-shared.conf.js @@ -0,0 +1,26 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +exports.config = { + specs: ['**/*.spec.coffee'], + baseUrl: 'http://localhost:8000/', + allScriptsTimeout: 30000, + getPageTimeout: 30000 +}; diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-travis.conf.js b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-travis.conf.js new file mode 100644 index 0000000000..54fe0bd828 --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-travis.conf.js @@ -0,0 +1,32 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +var config = require('./protractor-shared.conf').config; + +// All available platform / browser combinations can be found on https://saucelabs.com/platforms +config.multiCapabilities = [ + { + browserName: 'chrome', + platform: 'OS X 10.10', + version: '37' + } +]; + +exports.config = config; diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/test/spec/ng-infinite-scroll.spec.coffee b/catalog-ui/app/third-party/ng-infinite-scroll/test/spec/ng-infinite-scroll.spec.coffee new file mode 100644 index 0000000000..07e6fec8be --- /dev/null +++ b/catalog-ui/app/third-party/ng-infinite-scroll/test/spec/ng-infinite-scroll.spec.coffee @@ -0,0 +1,221 @@ +fs = require "fs" +mkdirp = require "mkdirp" + +getTemplate = (angularVersion, container, attrs, throttle) -> + """ + + + + + + + + + Enable + Force + Trigger + #{containers[container].start} +
+

+ {{$index}} +

+
+ #{containers[container].end} + + """ + +containers = + window: + start: "" + end: "" + attr: "" + parent: + start: "
" + end: "
" + attr: "infinite-scroll-parent" + ancestor: + start: "
" + end: "
" + attr: "infinite-scroll-container='\"#ancestor\"'" + +getElementByIdScript = (id) -> + "document.getElementById('#{id}')" + +calculateChildrenHeightScript = (id) -> + """ + [].concat.apply([], #{getElementByIdScript(id)}.childNodes) + .map(function (el) { return el.offsetHeight ? el.offsetHeight : 0; }) + .reduce(function (cur, prev) { return prev + cur; }, 0) + """ + +scrollToBottomScript = (container) -> + if container is "window" + "window.scrollTo(0, document.body.scrollHeight)" + else + "#{getElementByIdScript(container)}.scrollTop = #{calculateChildrenHeightScript(container)}" + +scrollToLastScreenScript = (container, offset) -> + # 2 * window.innerHeight means that the bottom of the screen should be somewhere close to + # body height - window height. That means that the top of the window is body height - 2 * window height. + if container is "window" + "window.scrollTo(0, document.body.scrollHeight - 2 * window.innerHeight + #{offset})" + else + """ + #{getElementByIdScript(container)}.scrollTop = + #{calculateChildrenHeightScript(container)} - 2 * #{getElementByIdScript(container)}.offsetHeight + #{offset} + """ + +collapseItemsScript = (container) -> + """ + var items = document.getElementsByTagName('p') + for (i = 0; i < items.length; ++i) { + items[i].style.display = 'none' + } + """ + +getItems = -> + element.all(By.repeater "item in items") + +tmpDir = ".tmp" +pathToDocument = "#{tmpDir}/index.html" + +describe "ng-infinite-scroll", -> + for angularVersion in ["1.2.0", "1.3.4"] + describe "with Angular #{angularVersion}", -> + for container in ["window", "ancestor", "parent"] + describe "with #{container} as container", -> + + replaceIndexFile = (attrs, throttle) -> + mkdirp tmpDir + fs.writeFileSync(pathToDocument, getTemplate(angularVersion, container, attrs, throttle)) + + describe "without throttling", -> + + throttle = null + + it "should be triggered immediately and when container is scrolled to the bottom", -> + replaceIndexFile "", throttle + browser.get pathToDocument + expect(getItems().count()).toBe 100 + browser.driver.executeScript(scrollToBottomScript(container)) + expect(getItems().count()).toBe 200 + + it "does not trigger immediately when infinite-scroll-immediate-check is false", -> + replaceIndexFile "infinite-scroll-immediate-check='false'", throttle + browser.get pathToDocument + expect(getItems().count()).toBe 0 + element(By.id("force")).click() + expect(getItems().count()).toBe 100 + browser.driver.executeScript(scrollToBottomScript(container)) + expect(getItems().count()).toBe 200 + + it "respects the disabled attribute", -> + replaceIndexFile "infinite-scroll-disabled='busy'", throttle + browser.get pathToDocument + expect(getItems().count()).toBe 0 + element(By.id("action")).click() + expect(getItems().count()).toBe 100 + + it "respects the infinite-scroll-distance attribute", -> + replaceIndexFile "infinite-scroll-distance='1'", throttle + browser.get pathToDocument + expect(getItems().count()).toBe 100 + browser.driver.executeScript(scrollToLastScreenScript(container, -20)) + expect(getItems().count()).toBe 100 + browser.driver.executeScript(scrollToLastScreenScript(container, 20)) + expect(getItems().count()).toBe 200 + + describe "with an event handler", -> + + it "calls the event handler on an event", -> + replaceIndexFile "infinite-scroll-listen-for-event='anEvent'", throttle + browser.get pathToDocument + expect(getItems().count()).toBe 100 + browser.driver.executeScript(collapseItemsScript(container)) + expect(getItems().count()).toBe 100 + element(By.id("trigger")).click() + expect(getItems().count()).toBe 200 + + describe "with throttling", -> + + throttle = browser.params.testThrottleValue + + it "should be triggered immediately and when container is scrolled to the bottom", -> + replaceIndexFile "", throttle + browser.get pathToDocument + expect(getItems().count()).toBe 100 + browser.driver.executeScript(scrollToBottomScript(container)) + expect(getItems().count()).toBe 100 + browser.sleep(throttle) + expect(getItems().count()).toBe 200 + + it "does not trigger immediately when infinite-scroll-immediate-check is false", -> + replaceIndexFile "infinite-scroll-immediate-check='false'", throttle + browser.get pathToDocument + expect(getItems().count()).toBe 0 + element(By.id("force")).click() + expect(getItems().count()).toBe 100 + + it "respects the disabled attribute and is throttled when page loads", -> + replaceIndexFile "infinite-scroll-disabled='busy'", throttle + browser.get pathToDocument + expect(getItems().count()).toBe 0 + element(By.id("action")).click() + expect(getItems().count()).toBe 0 + browser.sleep(throttle) + expect(getItems().count()).toBe 100 + + it "is not throttled when re-enabled if the throttle time has already elapsed", -> + replaceIndexFile "infinite-scroll-disabled='busy'", throttle + browser.get pathToDocument + expect(getItems().count()).toBe 0 + browser.sleep(throttle) + element(By.id("action")).click() + expect(getItems().count()).toBe 100 + + it "respects the infinite-scroll-distance attribute", -> + replaceIndexFile "infinite-scroll-distance='1'", throttle + browser.get pathToDocument + expect(getItems().count()).toBe 100 + browser.driver.executeScript(scrollToLastScreenScript(container, 20)) + expect(getItems().count()).toBe 100 + browser.sleep(throttle) + expect(getItems().count()).toBe 200 + + describe "with an event handler", -> + + it "calls the event handler on an event", -> + replaceIndexFile "infinite-scroll-listen-for-event='anEvent'", throttle + browser.get pathToDocument + expect(getItems().count()).toBe 100 + browser.driver.executeScript(collapseItemsScript(container)) + expect(getItems().count()).toBe 100 + element(By.id("trigger")).click() + expect(getItems().count()).toBe 100 + browser.sleep(throttle) + expect(getItems().count()).toBe 200 -- cgit