Merge pull request #105 from nextcloud/build

Update Build environment
This commit is contained in:
Raimund Schlüßler 2017-09-09 18:17:46 +02:00 коммит произвёл GitHub
Родитель c67cf0ab57 718ca78cec
Коммит dabd604bae
212 изменённых файлов: 16673 добавлений и 14543 удалений

Просмотреть файл

@ -7,7 +7,7 @@
# @author Raimund Schlüßler
# @copyright 2017 Raimund Schlüßler <raimund.schluessler@googlemail.com>
# Generic Makefile for building and packaging a Nextcloud app which uses npm and
# Generic Makefile for building and packaging a Nextcloud app which uses yarn and
# Composer.
#
# Dependencies:
@ -15,31 +15,31 @@
# * which
# * curl: used if phpunit and composer are not installed to fetch them from the web
# * tar: for building the archive
# * npm: for building and testing everything JS
# * yarn: for building and testing everything JS
#
# If no composer.json is in the app root directory, the Composer step
# will be skipped. The same goes for the package.json which can be located in
# the app root or the js/ directory.
#
# The npm command by launches the npm build script:
# The yarn command by launches the yarn build script:
#
# npm run build
# yarn run build
#
# The npm test command launches the npm test script:
# The yarn test command launches the yarn test script:
#
# npm run test
# yarn run test
#
# The idea behind this is to be completely testing and build tool agnostic. All
# build tools and additional package managers should be installed locally in
# your project, since this won't pollute people's global namespace.
#
# The following npm scripts in your package.json install and update the bower
# and npm dependencies and use gulp as build system (notice how everything is
# The following yarn scripts in your package.json install and update the
# yarn dependencies and use gulp as build system (notice how everything is
# run from the node_modules folder):
#
# "scripts": {
# "test": "node node_modules/gulp-cli/bin/gulp.js karma",
# "prebuild": "npm install && node_modules/bower/bin/bower install && node_modules/bower/bin/bower update",
# "prebuild": "yarn install && yarn upgrade",
# "build": "node node_modules/gulp-cli/bin/gulp.js"
# },
@ -51,7 +51,7 @@ source_package_name=$(source_artifact_directory)/$(app_name)
appstore_build_directory=$(CURDIR)/build/appstore/tasks
appstore_artifact_directory=$(CURDIR)/build/artifacts/appstore
appstore_package_name=$(appstore_artifact_directory)/$(app_name)
npm=$(shell which npm 2> /dev/null)
yarn=$(shell which yarn 2> /dev/null)
gcp=$(shell which gcp 2> /dev/null)
ifeq (, $(gcp))
@ -83,23 +83,22 @@ all: build
# Fetches the PHP and JS dependencies and compiles the JS. If no composer.json
# is present, the composer step is skipped, if no package.json or js/package.json
# is present, the npm step is skipped
# is present, the yarn step is skipped
.PHONY: build
build:
make npm
make yarn
# Installs npm dependencies
.PHONY: npm
npm:
cd js && $(npm) run build
# Installs yarn dependencies
.PHONY: yarn
yarn:
cd js && $(yarn) run build
# Removes the appstore build
.PHONY: clean
clean:
rm -rf ./build
# Same as clean but also removes dependencies installed by composer, bower and
# npm
# Same as clean but also removes dependencies installed by yarn
.PHONY: distclean
distclean: clean
rm -rf vendor
@ -218,7 +217,7 @@ endif
# from the internet
.PHONY: test
test:
cd js && $(npm) run test
cd js && $(yarn) run test
ifeq (, $(shell which phpunit 2> /dev/null))
@echo "No phpunit command available, downloading a copy from the web"
mkdir -p $(build_tools_directory)

Просмотреть файл

@ -1,7 +0,0 @@
{
"directory": "vendor",
"ignoredDependencies": [
"jquery",
"moment"
]
}

Просмотреть файл

@ -38,6 +38,7 @@
"exports": true,
"escapeHTML": true,
"possible": true,
"dav": true
"dav": true,
"OCA": true
}
}

15
js/.stylelintrc Normal file
Просмотреть файл

@ -0,0 +1,15 @@
{
"extends": "stylelint-config-standard",
"plugins": [
"stylelint-scss"
],
"rules": {
"indentation": "tab",
"number-leading-zero": "never",
"comment-empty-line-before": ["always", {
"except": ["first-nested"]
}],
"at-rule-no-unknown": null,
"scss/at-rule-no-unknown": true
}
}

Просмотреть файл

@ -1,130 +0,0 @@
/**
* Nextcloud - Tasks
*
* @author Raimund Schlüßler
* @copyright 2017 Raimund Schlüßler <raimund.schluessler@googlemail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
module.exports = function(grunt) {
'use strict';
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-wrap');
// grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-svg-sprite');
grunt.initConfig({
meta: {
pkg: grunt.file.readJSON('package.json'),
version: '<%= meta.pkg.version %>',
banner: '/**\n' + ' * <%= meta.pkg.description %> - v<%= meta.version %>\n' + ' *\n' + ' * Copyright (c) <%= grunt.template.today("yyyy") %> - ' + '<%= meta.pkg.author.name %> <<%= meta.pkg.author.email %>>\n' + ' *\n' + ' * This file is licensed under the Affero\
General Public License version 3 or later.\n' + ' * See the COPYING file\n' + ' *\n' + ' */\n\n',
build: 'app/',
production: 'public/'
},
concat: {
"default": {
options: {
banner: '<%= meta.banner %>\n',
stripBanners: {
options: 'block'
}
},
src: '<%= meta.build %>/**/*.js',
dest: '<%= meta.production %>app.js'
}
},
wrap: {
"default": {
src: '<%= meta.production %>app.js',
dest: '',
wrapper: ['(function(angular, $, oc_requesttoken, undefined){\n\n', '\n})(window.angular, window.jQuery, oc_requesttoken);']
}
},
jshint: {
files: [
'Gruntfile.js',
'<%= meta.build %>**/*.js'
],
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
}
},
watch: {
concat: {
files: [
'<%= meta.build %>**/*.js'
],
options: {
livereload: true
},
tasks: ['js']
}
},
karma: {
unit: {
configFile: 'config/karma.js'
},
continuous: {
configFile: 'config/karma.js',
singleRun: true,
reporters: ['progress', 'junit'],
junitReporter: {
outputFile: 'test-results.xml'
}
}
},
svg_sprite: {
basic: {
// Target basics
expand: true,
cwd: '../img/src',
src: ['**/*.svg'],
dest: '..',
// Target options
options: {
shape: {
transform: []
},
mode: {
css: { // Activate the «css» mode
bust: false,
common: 'icon',
dimensions: '',
prefix: '.ico-%s',
sprite: "../img/sprites.svg",
render: {
css: true // Activate CSS output (with default options)
}
}
}
}
}
}
});
// grunt.registerTask('ci', ['karma:continuous']);
grunt.registerTask('js', ['concat']);
grunt.registerTask('default', 'js');
grunt.registerTask('build', ['concat', 'svg_sprite']);
};

Просмотреть файл

@ -1,20 +0,0 @@
# JS & CSS development
You need grunt installed to compile the coffeescript and less files.
To compile all files run `grunt dev`.
## JS
To compile the coffeescript run:
`grunt js`
## CSS
To compile the less files run:
`grunt css`
## Watch Tasks
Instead of executing the command everytime you change a file, grunt can watch for changes.
Use the following commands instead:
```
grunt watch:dev
grunt watch:js
grunt watch:css
```

Просмотреть файл

@ -1,33 +0,0 @@
{
"name": "Nextcloud - Tasks",
"version": "0.9.5",
"dependencies": {
"angular": "1.5.5",
"angular-route": "1.5.5",
"angular-animate": "1.5.5",
"angular-sanitize": "1.5.5",
"angular-ui-select": "https://github.com/angular-ui/ui-select.git#v0.19.8",
"angular-draganddrop": "https://github.com/marceljuenemann/angular-drag-and-drop-lists.git#v2.1.0",
"jquery-timepicker": "",
"ical.js": "~1.2.2",
"jstzdetect": ""
},
"devDependencies": {},
"homepage": "https://github.com/nextcloud/tasks",
"authors": [
"Raimund Schlüßler <raimund.schluessler@googlemail.com>"
],
"description": "",
"main": "",
"moduleType": [],
"license": "AGPLv3",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"core/vendor",
"test",
"tests"
]
}

159
js/gulpfile.js Normal file
Просмотреть файл

@ -0,0 +1,159 @@
/**
* Nextcloud - Inventory
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Bernhard Posselt <dev@bernhard-posselt.com>
* @copyright Bernhard Posselt 2012, 2014
*
* @author Georg Ehrke
* @copyright 2017 Georg Ehrke <oc.list@georgehrke.com>
*
* @author Raimund Schlüßler
* @copyright 2017 Raimund Schlüßler <raimund.schluessler@googlemail.com>
*/
/*jslint node: true */
'use strict';
// get plugins
const gulp = require('gulp'),
uglify = require('gulp-uglify'),
jshint = require('gulp-jshint'),
KarmaServer = require('karma').Server,
concat = require('gulp-concat'),
wrap = require('gulp-wrap'),
strip = require('gulp-strip-banner'),
babel = require('gulp-babel'),
stylelint = require('gulp-stylelint'),
sourcemaps = require('gulp-sourcemaps'),
svgSprite = require('gulp-svg-sprite'),
npmFiles = require('gulp-npm-files');
// configure
const buildTarget = 'app.min.js';
const scssBuildTarget = 'style.scss';
const karmaConfig = __dirname + '/../tests/js/config/karma.js';
const destinationFolder = __dirname + '/public/';
const scssDestinationFolder = '../css/';
const jsSources = [
'app/**/*.js'
];
const scssSources = [
'../css/src/*.scss'
];
const testSources = ['../tests/js/unit/**/*.js'];
const lintSources = jsSources.concat(testSources).concat(['*.js']);
const watchSources = lintSources;
const svgConfig = {
shape: {
transform: []
},
mode: {
css: { // Activate the «css» mode
bust: false,
common: 'icon',
dimensions: '',
prefix: '.ico-%s',
sprite: "../img/sprites.svg",
render: {
css: {
dest: "../css/sprite.css"
}
}
}
}
};
// tasks
gulp.task('build', ['lint'], () => {
return gulp.src(jsSources)
.pipe(babel({
presets: ['es2015'],
compact: false,
babelrc: false,
ast: false
}))
.pipe(strip())
.pipe(sourcemaps.init({identityMap: true}))
.pipe(concat(buildTarget))
.pipe(wrap(`(function($, oc_requesttoken, undefined){
'use strict';
<%= contents %>
})(jQuery, oc_requesttoken);`))
.pipe(uglify())
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(destinationFolder));
});
// gulp.task('default', ['build', 'vendor', 'scsslint', 'scssConcat']);
gulp.task('default', ['build', 'vendor', 'svg_sprite']);
gulp.task('lint', () => {
return gulp.src(lintSources)
.pipe(jshint('.jshintrc'))
.pipe(jshint.reporter('default'));
// .pipe(jshint.reporter('fail'));
});
gulp.task('scsslint', () => {
return gulp.src(scssSources)
.pipe(stylelint ({
reporters: [{
formatter: 'string',
console: true
}]
}));
});
gulp.task('scssConcat', ['svg_sprite'], () => {
return gulp.src(scssSources)
.pipe(concat(scssBuildTarget))
.pipe(gulp.dest(scssDestinationFolder));
});
gulp.task('scssConcatWatch', () => {
return gulp.src(scssSources)
.pipe(concat(scssBuildTarget))
.pipe(gulp.dest(scssDestinationFolder));
});
gulp.task('watch', () => {
gulp.watch(watchSources, ['build']);
gulp.watch(scssSources, ['scssConcatWatch']);
});
gulp.task('karma', (done) => {
new KarmaServer({
configFile: karmaConfig,
singleRun: true,
browsers: ['Firefox'],
reporters: ['progress', 'coverage']
}, done).start();
});
gulp.task('watch-karma', (done) => {
new KarmaServer({
configFile: karmaConfig,
autoWatch: true,
browsers: ['Firefox'],
reporters: ['progress', 'coverage']
}, done).start();
});
gulp.task('svg_sprite', () => {
return gulp.src('**/*.svg', {cwd: '../img/src'})
.pipe(svgSprite(svgConfig))
.pipe(gulp.dest('..'));
});
gulp.task("vendor", () => {
return gulp.src(npmFiles(), { base: "./node_modules" })
.pipe(gulp.dest('vendor'));
});

Просмотреть файл

@ -6,12 +6,13 @@
"name": "Raimund Schlüßler",
"email": "raimund.schluessler@googlemail.com"
},
"license": "AGPLv3",
"private": true,
"homepage": "",
"homepage": "https://github.com/nextcloud/tasks",
"scripts": {
"test": "node node_modules/grunt-cli/bin/grunt karma",
"prebuild": "npm install && npm update && node_modules/bower/bin/bower install && node_modules/bower/bin/bower update",
"build": "node node_modules/grunt-cli/bin/grunt build"
"test": "node node_modules/gulp-cli/bin/gulp.js karma",
"prebuild": "yarn install && yarn upgrade",
"build": "node node_modules/gulp-cli/bin/gulp.js"
},
"repository": {
"type": "git",
@ -19,19 +20,36 @@
},
"bugs": "https://github.com/nextcloud/tasks/issues",
"contributors": [],
"dependencies": {},
"dependencies": {
"angular": "1.5.5",
"angular-animate": "1.5.5",
"angular-draganddrop": "https://github.com/marceljuenemann/angular-drag-and-drop-lists.git#v2.1.0",
"angular-route": "1.5.5",
"angular-sanitize": "1.5.5",
"angular-ui-select": "https://github.com/angular-ui/ui-select.git#v0.19.8",
"ical.js": "~1.2.2",
"jstimezonedetect": ""
},
"devDependencies": {
"bower": "*",
"grunt": "^1.0.1",
"grunt-cli": "^1.2.0",
"grunt-contrib-jshint": "^1.0.0",
"jshint-stylish": "^2.2.1",
"grunt-concurrent": "^2.3.1",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-watch": "^1.0.0",
"grunt-karma": "^2.0.0",
"grunt-wrap": "^0.3.0",
"grunt-svg-sprite": "1.3.7"
"babel-preset-es2015": "^6.14.0",
"gulp": "^3.9.1",
"gulp-babel": "^6.1.2",
"gulp-cli": "^1.2.2",
"gulp-concat": "^2.6.1",
"gulp-jshint": "^2.0.4",
"gulp-ng-annotate": "^2.0.0",
"gulp-npm-files": "^0.1.3",
"gulp-sourcemaps": "^2.4.0",
"gulp-strip-banner": "0.0.2",
"gulp-stylelint": "^4.0.0",
"gulp-svg-sprite": "1.3.7",
"gulp-uglify": "^2.0.0",
"gulp-wrap": "^0.13.0",
"jshint": "^2.9.4",
"karma": "^1.3.0",
"stylelint": "^8.0.0",
"stylelint-config-standard": "^17.0.0",
"stylelint-scss": "^2.1.0"
},
"engines": {
"node": ">=6"

Разница между файлами не показана из-за своего большого размера Загрузить разницу

4
js/public/app.min.js поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

1
js/public/app.min.js.map Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

20
js/vendor/angular-animate/.bower.json поставляемый
Просмотреть файл

@ -1,20 +0,0 @@
{
"name": "angular-animate",
"version": "1.5.5",
"license": "MIT",
"main": "./angular-animate.js",
"ignore": [],
"dependencies": {
"angular": "1.5.5"
},
"homepage": "https://github.com/angular/bower-angular-animate",
"_release": "1.5.5",
"_resolution": {
"type": "version",
"tag": "v1.5.5",
"commit": "39c4ea7a81ed05b09229f5961e31e1d9dc251bf8"
},
"_source": "https://github.com/angular/bower-angular-animate.git",
"_target": "1.5.5",
"_originalSource": "angular-animate"
}

39
js/vendor/angular-draganddrop/.bower.json поставляемый
Просмотреть файл

@ -1,39 +0,0 @@
{
"name": "angular-drag-and-drop-lists",
"main": "angular-drag-and-drop-lists.js",
"version": "2.1.0",
"homepage": "https://github.com/marceljuenemann/angular-drag-and-drop-lists",
"authors": [
"Marcel Juenemann <marcel@juenemann.cc>"
],
"description": "Angular directives for sorting nested lists using the HTML5 Drag & Drop API",
"keywords": [
"angular",
"drag",
"drop",
"dnd",
"nested",
"sortable",
"lists",
"html5"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"demo",
"*.json",
"test",
"tests"
],
"_release": "2.1.0",
"_resolution": {
"type": "version",
"tag": "2.1.0",
"commit": "7e98e194a8d66fa735bf6dc7c3e39886287b914e"
},
"_source": "https://github.com/marceljuenemann/angular-drag-and-drop-lists.git",
"_target": "v2.1.0",
"_originalSource": "https://github.com/marceljuenemann/angular-drag-and-drop-lists.git"
}

28
js/vendor/angular-draganddrop/demo/advanced/advanced-frame.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,28 @@
<h1>Demo: Advanced Features</h1>
<ul>
<li><strong>dnd-effect-allowed:</strong> This demo shows how to use dnd-effect-allowed to control which drop effects are allowed.
If the source and target elements have no drop effect that is allowed on both, then a drop is not possible. If there are multiple
possible drop effects, then the user can control the drop effect using modifier keys (Ctrl and Alt).</li>
<li><strong>dnd-external-sources:</strong> Allows to drag and drop elements accross browser windows, which you can test in this
example. The downside to this is that the lists will accept arbitrary text to be dropped. To prevent that, the dnd-drop callback
verifies that the dropped element is of the desired format.</li>
<li><strong>dnd-allowed-types in nested lists:</strong> We are using the dnd-allowed-types attribute to ensure that Containers
only accept items, but not other containers.</li>
<li><strong>dnd-horizontal-list:</strong> This attribute tells the positioning algorithm to drop incoming elements left or right
of the existing elements, instead of above or below.</li>
<li><strong>Callbacks:</strong> The directives offer various callbacks, which in this example will log the events to the console.
Additionally, the callbacks on the dnd-list can prevent an element from being dropped. In this example <strong>you can't drop elements
after the 10th position</strong>, because we are preventing that in the dnd-dragover callback.</li>
</ul>
<div class="advancedDemo row">
<div ng-repeat="containers in model" class="col-md-6">
<div class="dropzone box box-yellow" ng-include="'advanced/advanced.html'"></div>
</div>
</div>
<div view-source="advanced"></div>
<h2>Generated Model</h2>
<pre>{{modelAsJson}}</pre>

66
js/vendor/angular-draganddrop/demo/advanced/advanced.css поставляемый Normal file
Просмотреть файл

@ -0,0 +1,66 @@
/***************************** Dropzone Styling *****************************/
/**
* The dnd-list should always have a min-height,
* otherwise you can't drop to it once it's empty
*/
.advancedDemo .dropzone ul[dnd-list] {
min-height: 42px;
margin: 0px;
padding-left: 0px;
}
.advancedDemo .dropzone li {
display: block;
}
/**
* Reduce opacity of elements during the drag operation. This allows the user
* to see where he is dropping his element, even if the element is huge. The
* .dndDragging class is automatically set during the drag operation.
*/
.advancedDemo .dropzone .dndDragging {
opacity: 0.7;
}
/**
* The dndDraggingSource class will be applied to the source element of a drag
* operation.
*/
.advancedDemo .dropzone .dndDraggingSource {
opacity: 0.5;
}
/**
* An element with .dndPlaceholder class will be added as child of the dnd-list
* while the user is dragging over it.
*/
.advancedDemo .dropzone .dndPlaceholder {
background-color: #ddd !important;
display: block;
min-height: 42px;
}
/***************************** Element type specific styles *****************************/
.advancedDemo .dropzone .itemlist {
min-height: 120px !important;
}
.advancedDemo .dropzone .itemlist > li {
background-color: #337ab7;
border: none;
border-radius: .25em;
color: #fff;
float: left;
font-weight: 700;
height: 50px;
margin: 5px;
padding: 3px;
text-align: center;
width: 80px;
}
.advancedDemo .dropzone .container-element {
margin: 10px;
}

37
js/vendor/angular-draganddrop/demo/advanced/advanced.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,37 @@
<h3>Dropzone {{$index + 1}}</h3>
<ul dnd-list="containers"
dnd-allowed-types="['container']"
dnd-external-sources="true"
dnd-dragover="dragoverCallback(index, external, type, callback)"
dnd-drop="dropCallback(index, item, external, type)">
<li ng-repeat="container in containers"
dnd-draggable="container"
dnd-type="'container'"
dnd-effect-allowed="copyMove"
dnd-moved="containers.splice($index, 1)"
dnd-callback="container.items.length">
<div class="container-element box box-blue">
<h3>Container (effects allowed: {{container.effectAllowed}})</h3>
<ul dnd-list="container.items"
dnd-allowed-types="['item']"
dnd-horizontal-list="true"
dnd-external-sources="true"
dnd-effect-allowed="{{container.effectAllowed}}"
dnd-dragover="dragoverCallback(index, external, type)"
dnd-drop="dropCallback(index, item, external, type)"
dnd-inserted="logListEvent('inserted at', index, external, type)"
class="itemlist">
<li ng-repeat="item in container.items"
dnd-draggable="item"
dnd-type="'item'"
dnd-effect-allowed="{{item.effectAllowed}}"
dnd-dragstart="logEvent('Started to drag an item')"
dnd-moved="container.items.splice($index, 1)"
dnd-dragend="logEvent('Drag operation ended. Drop effect: ' + dropEffect)">
{{item.label}}
</li>
</ul>
<div class="clearfix"></div>
</div>
</li>
</ul>

43
js/vendor/angular-draganddrop/demo/advanced/advanced.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,43 @@
angular.module("demo").controller("AdvancedDemoController", function($scope) {
$scope.dragoverCallback = function(index, external, type, callback) {
$scope.logListEvent('dragged over', index, external, type);
// Invoke callback to origin for container types.
if (type == 'container' && !external) {
console.log('Container being dragged contains ' + callback() + ' items');
}
return index < 10; // Disallow dropping in the third row.
};
$scope.dropCallback = function(index, item, external, type) {
$scope.logListEvent('dropped at', index, external, type);
// Return false here to cancel drop. Return true if you insert the item yourself.
return item;
};
$scope.logEvent = function(message) {
console.log(message);
};
$scope.logListEvent = function(action, index, external, type) {
var message = external ? 'External ' : '';
message += type + ' element was ' + action + ' position ' + index;
console.log(message);
};
// Initialize model
$scope.model = [[], []];
var id = 10;
angular.forEach(['all', 'move', 'copy', 'link', 'copyLink', 'copyMove'], function(effect, i) {
var container = {items: [], effectAllowed: effect};
for (var k = 0; k < 7; ++k) {
container.items.push({label: effect + ' ' + id++, effectAllowed: effect});
}
$scope.model[i % $scope.model.length].push(container);
});
$scope.$watch('model', function(model) {
$scope.modelAsJson = angular.toJson(model, true);
}, true);
});

70
js/vendor/angular-draganddrop/demo/framework/demo-framework.css поставляемый Normal file
Просмотреть файл

@ -0,0 +1,70 @@
body {
padding-top: 70px;
padding-bottom: 30px;
}
.box {
margin-bottom: 20px;
background-color: #fff;
border: 1px solid transparent;
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);
box-shadow: 0 1px 2px rgba(0,0,0,.05);
}
.box > h3 {
color: #333;
border-color: #ddd;
border-bottom: 1px solid transparent;
border-top-right-radius: 3px;
border-top-left-radius: 3px;
background-repeat: repeat-x;
display: block;
font-size: 16px;
padding: 10px 15px;
margin-top: 0;
margin-bottom: 0;
}
.box-padding {
padding: 15px;
}
.box-padding > h3 {
margin: -15px;
margin-bottom: 15px;
}
.box-grey {
border-color: #ddd;
}
.box-grey > h3 {
background-color: #f5f5f5;
background-image: -webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);
background-image: linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);
}
.box-blue {
border-color: #bce8f1;
}
.box-blue > h3 {
color: #31708f;
background-color: #d9edf7;
border-color: #bce8f1;
background-image: -webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);
background-image: linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);
}
.box-yellow {
border-color: #faebcc;
}
.box-yellow > h3 {
color: #8a6d3b;
background-color: #fcf8e3;
border-color: #faebcc;
background-image: -webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);
background-image: linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);
}

51
js/vendor/angular-draganddrop/demo/framework/demo-framework.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,51 @@
angular.module("demo", ["ngRoute", "dndLists"])
.config(function($routeProvider) {
$routeProvider
.when('/simple', {
templateUrl: 'simple/simple-frame.html',
controller: 'SimpleDemoController'
})
.when('/nested', {
templateUrl: 'nested/nested-frame.html',
controller: 'NestedListsDemoController'
})
.when('/types', {
templateUrl: 'types/types-frame.html',
controller: 'TypesDemoController'
})
.when('/advanced', {
templateUrl: 'advanced/advanced-frame.html',
controller: 'AdvancedDemoController'
})
.when('/multi', {
templateUrl: 'multi/multi-frame.html',
controller: 'MultiDemoController'
})
.otherwise({redirectTo: '/nested'});
})
.directive('navigation', function($rootScope, $location) {
return {
template: '<li ng-repeat="option in options" ng-class="{active: isActive(option)}">' +
' <a ng-href="{{option.href}}">{{option.label}}</a>' +
'</li>',
link: function (scope, element, attr) {
scope.options = [
{label: "Nested Containers", href: "#/nested"},
{label: "Simple Demo", href: "#/simple"},
{label: "Item Types", href: "#/types"},
{label: "Advanced Demo", href: "#/advanced"},
{label: "Multiselection", href: "#/multi"},
{label: "Github", href: "https://github.com/marceljuenemann/angular-drag-and-drop-lists"}
];
scope.isActive = function(option) {
return option.href.indexOf(scope.location) === 1;
};
$rootScope.$on("$locationChangeSuccess", function(event, next, current) {
scope.location = $location.path();
});
}
};
});

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Двоичные данные
js/vendor/angular-draganddrop/demo/framework/vendor/ic_content_copy_black_24dp_2x.png поставляемый Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 180 B

213
js/vendor/angular-draganddrop/demo/framework/vendor/prism.css поставляемый Normal file
Просмотреть файл

@ -0,0 +1,213 @@
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.builtin {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #a67f59;
background: hsla(0,0%,100%,.5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.regex,
.token.important {
color: #e90;
}
.token.important {
font-weight: bold;
}
.token.entity {
cursor: help;
}
pre[data-line] {
position: relative;
padding: 1em 0 1em 3em;
}
.line-highlight {
position: absolute;
left: 0;
right: 0;
padding: inherit 0;
margin-top: 1em; /* Same as .prisms padding-top */
background: hsla(24, 20%, 50%,.08);
background: -moz-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
background: -webkit-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
background: -o-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
background: linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
pointer-events: none;
line-height: inherit;
white-space: pre;
}
.line-highlight:before,
.line-highlight[data-end]:after {
content: attr(data-start);
position: absolute;
top: .4em;
left: .6em;
min-width: 1em;
padding: 0 .5em;
background-color: hsla(24, 20%, 50%,.4);
color: hsl(24, 20%, 95%);
font: bold 65%/1.5 sans-serif;
text-align: center;
vertical-align: .3em;
border-radius: 999px;
text-shadow: none;
box-shadow: 0 1px white;
}
.line-highlight[data-end]:after {
content: attr(data-end);
top: auto;
bottom: .4em;
}
pre.line-numbers {
position: relative;
padding-left: 3.8em;
counter-reset: linenumber;
}
pre.line-numbers > code {
position: relative;
}
.line-numbers .line-numbers-rows {
position: absolute;
pointer-events: none;
top: 0;
font-size: 100%;
left: -3.8em;
width: 3em; /* works for line-numbers below 1000 lines */
letter-spacing: -1px;
border-right: 1px solid #999;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.line-numbers-rows > span {
pointer-events: none;
display: block;
counter-increment: linenumber;
}
.line-numbers-rows > span:before {
content: counter(linenumber);
color: #999;
display: block;
padding-right: 0.8em;
text-align: right;
}

14
js/vendor/angular-draganddrop/demo/framework/vendor/prism.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,14 @@
/**
* Prism: Lightweight, robust, elegant syntax highlighting
* MIT license http://www.opensource.org/licenses/mit-license.php/
* @author Lea Verou http://lea.verou.me
*/(function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var r={};for(var i in e)e.hasOwnProperty(i)&&(r[i]=t.util.clone(e[i]));return r;case"Array":return e.slice()}return e}},languages:{extend:function(e,n){var r=t.util.clone(t.languages[e]);for(var i in n)r[i]=n[i];return r},insertBefore:function(e,n,r,i){i=i||t.languages;var s=i[e],o={};for(var u in s)if(s.hasOwnProperty(u)){if(u==n)for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);o[u]=s[u]}return i[e]=o},DFS:function(e,n){for(var r in e){n.call(e,r,e[r]);t.util.type(e)==="Object"&&t.languages.DFS(e[r],n)}}},highlightAll:function(e,n){var r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');for(var i=0,s;s=r[i++];)t.highlightElement(s,e===!0,n)},highlightElement:function(r,i,s){var o,u,a=r;while(a&&!e.test(a.className))a=a.parentNode;if(a){o=(a.className.match(e)||[,""])[1];u=t.languages[o]}if(!u)return;r.className=r.className.replace(e,"").replace(/\s+/g," ")+" language-"+o;a=r.parentNode;/pre/i.test(a.nodeName)&&(a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+o);var f=r.textContent;if(!f)return;f=f.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ");var l={element:r,language:o,grammar:u,code:f};t.hooks.run("before-highlight",l);if(i&&self.Worker){var c=new Worker(t.filename);c.onmessage=function(e){l.highlightedCode=n.stringify(JSON.parse(e.data),o);t.hooks.run("before-insert",l);l.element.innerHTML=l.highlightedCode;s&&s.call(l.element);t.hooks.run("after-highlight",l)};c.postMessage(JSON.stringify({language:l.language,code:l.code}))}else{l.highlightedCode=t.highlight(l.code,l.grammar,l.language);t.hooks.run("before-insert",l);l.element.innerHTML=l.highlightedCode;s&&s.call(r);t.hooks.run("after-highlight",l)}},highlight:function(e,r,i){return n.stringify(t.tokenize(e,r),i)},tokenize:function(e,n,r){var i=t.Token,s=[e],o=n.rest;if(o){for(var u in o)n[u]=o[u];delete n.rest}e:for(var u in n){if(!n.hasOwnProperty(u)||!n[u])continue;var a=n[u],f=a.inside,l=!!a.lookbehind,c=0;a=a.pattern||a;for(var h=0;h<s.length;h++){var p=s[h];if(s.length>e.length)break e;if(p instanceof i)continue;a.lastIndex=0;var d=a.exec(p);if(d){l&&(c=d[1].length);var v=d.index-1+c,d=d[0].slice(c),m=d.length,g=v+m,y=p.slice(0,v+1),b=p.slice(g+1),w=[h,1];y&&w.push(y);var E=new i(u,f?t.tokenize(d,f):d);w.push(E);b&&w.push(b);Array.prototype.splice.apply(s,w)}}}return s},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[];r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(!r||!r.length)return;for(var i=0,s;s=r[i++];)s(n)}}},n=t.Token=function(e,t){this.type=e;this.content=t};n.stringify=function(e,r,i){if(typeof e=="string")return e;if(Object.prototype.toString.call(e)=="[object Array]")return e.map(function(t){return n.stringify(t,r,e)}).join("");var s={type:e.type,content:n.stringify(e.content,r,i),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:i};s.type=="comment"&&(s.attributes.spellcheck="true");t.hooks.run("wrap",s);var o="";for(var u in s.attributes)o+=u+'="'+(s.attributes[u]||"")+'"';return"<"+s.tag+' class="'+s.classes.join(" ")+'" '+o+">"+s.content+"</"+s.tag+">"};if(!self.document){self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,i=n.code;self.postMessage(JSON.stringify(t.tokenize(i,t.languages[r])));self.close()},!1);return}var r=document.getElementsByTagName("script");r=r[r.length-1];if(r){t.filename=r.src;document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)}})();;
Prism.languages.markup={comment:/&lt;!--[\w\W]*?-->/g,prolog:/&lt;\?.+?\?>/,doctype:/&lt;!DOCTYPE.+?>/,cdata:/&lt;!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/&lt;\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|\w+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^&lt;\/?[\w:-]+/i,inside:{punctuation:/^&lt;\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/&amp;#?[\da-z]{1,8};/gi};Prism.hooks.add("wrap",function(e){e.type==="entity"&&(e.attributes.title=e.content.replace(/&amp;/,"&"))});;
Prism.languages.css={comment:/\/\*[\w\W]*?\*\//g,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*{))/gi,inside:{punctuation:/[;:]/g}},url:/url\((["']?).*?\1\)/gi,selector:/[^\{\}\s][^\{\};]*(?=\s*\{)/g,property:/(\b|\B)[\w-]+(?=\s*:)/ig,string:/("|')(\\?.)*?\1/g,important:/\B!important\b/gi,ignore:/&(lt|gt|amp);/gi,punctuation:/[\{\};:]/g};Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{style:{pattern:/(&lt;|<)style[\w\W]*?(>|&gt;)[\w\W]*?(&lt;|<)\/style(>|&gt;)/ig,inside:{tag:{pattern:/(&lt;|<)style[\w\W]*?(>|&gt;)|(&lt;|<)\/style(>|&gt;)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.css}}});;
Prism.languages.clike={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,lookbehind:!0},string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/ig,inside:{punctuation:/\(/}}, number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|&lt;=?|>=?|={1,3}|(&amp;){1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};
;
Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(var|let|if|else|while|do|for|return|in|instanceof|function|get|set|new|with|typeof|try|throw|catch|finally|null|break|continue)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g});Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}});Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/(&lt;|<)script[\w\W]*?(>|&gt;)[\w\W]*?(&lt;|<)\/script(>|&gt;)/ig,inside:{tag:{pattern:/(&lt;|<)script[\w\W]*?(>|&gt;)|(&lt;|<)\/script(>|&gt;)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}});;
(function(){if(!window.Prism){return}function $$(a,b){return Array.prototype.slice.call((b||document).querySelectorAll(a))}function hasClass(a,b){b=" "+b+" ";return(" "+a.className+" ").replace(/[\n\t]/g," ").indexOf(b)>-1}var h=crlf=/\r?\n|\r/g;function highlightLines(a,b,c){var d=b.replace(/\s+/g,'').split(','),offset=+a.getAttribute('data-line-offset')||0;var e=parseFloat(getComputedStyle(a).lineHeight);for(var i=0,range;range=d[i++];){range=range.split('-');var f=+range[0],end=+range[1]||f;var g=document.createElement('div');g.textContent=Array(end-f+2).join(' \r\n');g.className=(c||'')+' line-highlight';if(!hasClass(a,'line-numbers')){g.setAttribute('data-start',f);if(end>f){g.setAttribute('data-end',end)}}g.style.top=(f-offset-1)*e+'px';if(hasClass(a,'line-numbers')){a.appendChild(g)}else{(a.querySelector('code')||a).appendChild(g)}}}function applyHash(){var b=location.hash.slice(1);$$('.temporary.line-highlight').forEach(function(a){a.parentNode.removeChild(a)});var c=(b.match(/\.([\d,-]+)$/)||[,''])[1];if(!c||document.getElementById(b)){return}var d=b.slice(0,b.lastIndexOf('.')),pre=document.getElementById(d);if(!pre){return}if(!pre.hasAttribute('data-line')){pre.setAttribute('data-line','')}highlightLines(pre,c,'temporary ');document.querySelector('.temporary.line-highlight').scrollIntoView()}var j=0;Prism.hooks.add('after-highlight',function(b){var c=b.element.parentNode;var d=c&&c.getAttribute('data-line');if(!c||!d||!/pre/i.test(c.nodeName)){return}clearTimeout(j);$$('.line-highlight',c).forEach(function(a){a.parentNode.removeChild(a)});highlightLines(c,d);j=setTimeout(applyHash,1)});addEventListener('hashchange',applyHash)})();
;
Prism.hooks.add("after-highlight",function(e){var t=e.element.parentNode;if(!t||!/pre/i.test(t.nodeName)||t.className.indexOf("line-numbers")===-1){return}var n=1+e.code.split("\n").length;var r;lines=new Array(n);lines=lines.join("<span></span>");r=document.createElement("span");r.className="line-numbers-rows";r.innerHTML=lines;if(t.hasAttribute("data-start")){t.style.counterReset="linenumber "+(parseInt(t.getAttribute("data-start"),10)-1)}e.element.appendChild(r)})
;

17
js/vendor/angular-draganddrop/demo/framework/view-source.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
<h2>Source Code</h2>
<ul class="nav nav-tabs">
<li ng-repeat="type in models.types"
ng-click="models.activeTab = type.language"
ng-class="{active: models.activeTab === type.language}">
<a style="cursor:pointer;">{{type.label}}</a>
</li>
</ul>
<div class="tab-content">
<div ng-repeat="type in models.types"
ng-class="{active: models.activeTab === type.language}"
class="tab-pane" >
<pre class="line-numbers" data-line="{{highlightLines[type.language]}}"><code class="language-{{type.language}}">{{type.source}}</code></pre>
</div>
</div>

29
js/vendor/angular-draganddrop/demo/framework/view-source.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,29 @@
angular.module("demo").directive('viewSource', function($http, $timeout) {
return {
scope: {
demoName: "@viewSource",
highlightLines: "="
},
templateUrl: 'framework/view-source.html',
link: function (scope, element, attr) {
scope.models = {
types: [
{extension: "html", language: "markup", label: "Markup"},
{extension: "css", language: "css", label: "CSS"},
{extension: "js", language: "javascript", label: "Javascript"},
],
activeTab: "markup"
};
angular.forEach(scope.models.types, function(type) {
$http.get(scope.demoName + '/' + scope.demoName + '.' + type.extension)
.success(function (data) {
type.source = data;
$timeout(Prism.highlightAll, 0);
});
});
}
};
});

65
js/vendor/angular-draganddrop/demo/index.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Drag &amp; Drop Lists for angular.js</title>
<!-- jQuery is not required -->
<!-- <script src="//code.jquery.com/jquery-1.11.0.min.js"></script> -->
<!-- angular is the only dependency! -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="../angular-drag-and-drop-lists.js"></script>
<!-- Stuff that is only required in this demo (no need to copy) -->
<link rel="stylesheet" href="framework/vendor/bootstrap.min.css">
<link rel="stylesheet" href="framework/vendor/bootstrap-theme.min.css">
<link rel="stylesheet" href="framework/vendor/prism.css">
<link rel="stylesheet" href="framework/demo-framework.css">
<link rel="stylesheet" type="text/css" href="simple/simple.css" />
<link rel="stylesheet" type="text/css" href="nested/nested.css" />
<link rel="stylesheet" type="text/css" href="types/types.css" />
<link rel="stylesheet" type="text/css" href="advanced/advanced.css" />
<link rel="stylesheet" type="text/css" href="multi/multi.css" />
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-route.min.js"></script>
<script src="framework/vendor/prism.js"></script>
<script src="framework/demo-framework.js"></script>
<script src="framework/view-source.js"></script>
<script src="simple/simple.js"></script>
<script src="nested/nested.js"></script>
<script src="types/types.js"></script>
<script src="advanced/advanced.js"></script>
<script src="multi/multi.js"></script>
</head>
<body ng-app="demo">
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">angular-drag-and-drop-lists</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav" navigation></ul>
</div>
</div>
</div>
<div class="container">
<div class="jumbotron">
<h1>Angular drag & drop with HTML5</h1>
<p>
Directives for modifying lists with the HTML5 drag & drop API.
Supports nested lists for building trees and other fancy structures.
No boilerplate code, no jQuery, just a few KB!
</p>
<p><a href="https://github.com/marceljuenemann/angular-drag-and-drop-lists" class="btn btn-success btn-lg" role="button">Code on github &raquo;</a></p>
</div>
<div ng-view></div>
</div>
</body>

37
js/vendor/angular-draganddrop/demo/multi/multi-frame.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,37 @@
<h1>Demo: Multiselect Lists</h1>
<div class="alert alert-success">
<strong>Instructions:</strong>
Click on items to select/unselect them. When dragging, all selected items will be moved at once.
</div>
<div class="multiDemo row">
<div class="col-md-8">
<div class="row">
<div ng-repeat="list in models" class="col-md-6">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">List {{list.listName}}</h3>
</div>
<div class="panel-body" ng-include="'multi/multi.html'"></div>
</div>
</div>
</div>
<div view-source="multi"></div>
</div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Generated Model</h3>
</div>
<div class="panel-body">
<pre class="code">{{modelAsJson}}</pre>
</div>
</div>
</div>
</div>

37
js/vendor/angular-draganddrop/demo/multi/multi.css поставляемый Normal file
Просмотреть файл

@ -0,0 +1,37 @@
/**
* The dnd-list should always have a min-height,
* otherwise you can't drop into it once it's empty
*/
.multiDemo ul[dnd-list] {
min-height: 42px;
padding-left: 0px;
}
/**
* An element with .dndPlaceholder class will be
* added to the dnd-list while the user is dragging
* over it.
*/
.multiDemo ul[dnd-list] .dndPlaceholder {
background-color: #ddd;
display: block;
min-height: 42px;
}
.multiDemo ul[dnd-list] li {
background-color: #fff;
border: 1px solid #ddd;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
display: block;
margin-bottom: -1px;
padding: 10px 15px;
}
/**
* Show selected elements in green
*/
.multiDemo ul[dnd-list] li.selected {
background-color: #dff0d8;
color: #3c763d;
}

13
js/vendor/angular-draganddrop/demo/multi/multi.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,13 @@
<ul dnd-list dnd-drop="onDrop(list, item, index)">
<li ng-repeat="item in list.items"
dnd-draggable="getSelectedItemsIncluding(list, item)"
dnd-dragstart="onDragstart(list, event)"
dnd-moved="onMoved(list)"
dnd-dragend="list.dragging = false"
dnd-selected="item.selected = !item.selected"
ng-class="{'selected': item.selected}"
ng-hide="list.dragging && item.selected"
>
{{item.label}}
</li>
</ul>

66
js/vendor/angular-draganddrop/demo/multi/multi.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,66 @@
angular.module("demo").controller("MultiDemoController", function($scope) {
$scope.models = [
{listName: "A", items: [], dragging: false},
{listName: "B", items: [], dragging: false}
];
/**
* dnd-dragging determines what data gets serialized and send to the receiver
* of the drop. While we usually just send a single object, we send the array
* of all selected items here.
*/
$scope.getSelectedItemsIncluding = function(list, item) {
item.selected = true;
return list.items.filter(function(item) { return item.selected; });
};
/**
* We set the list into dragging state, meaning the items that are being
* dragged are hidden. We also use the HTML5 API directly to set a custom
* image, since otherwise only the one item that the user actually dragged
* would be shown as drag image.
*/
$scope.onDragstart = function(list, event) {
list.dragging = true;
if (event.dataTransfer.setDragImage) {
var img = new Image();
img.src = 'framework/vendor/ic_content_copy_black_24dp_2x.png';
event.dataTransfer.setDragImage(img, 0, 0);
}
};
/**
* In the dnd-drop callback, we now have to handle the data array that we
* sent above. We handle the insertion into the list ourselves. By returning
* true, the dnd-list directive won't do the insertion itself.
*/
$scope.onDrop = function(list, items, index) {
angular.forEach(items, function(item) { item.selected = false; });
list.items = list.items.slice(0, index)
.concat(items)
.concat(list.items.slice(index));
return true;
}
/**
* Last but not least, we have to remove the previously dragged items in the
* dnd-moved callback.
*/
$scope.onMoved = function(list) {
list.items = list.items.filter(function(item) { return !item.selected; });
};
// Generate the initial model
angular.forEach($scope.models, function(list) {
for (var i = 1; i <= 4; ++i) {
list.items.push({label: "Item " + list.listName + i});
}
});
// Model to JSON for demo purpose
$scope.$watch('models', function(model) {
$scope.modelAsJson = angular.toJson(model, true);
}, true);
});

10
js/vendor/angular-draganddrop/demo/nested/nested-frame.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,10 @@
<h1>Demo: Nested Containers</h1>
<div class="alert alert-success">
<strong>Instructions:</strong>
In this demo you can not only drag &amp; drop list items, but also containers, which
can contain list items or other containers themselves. To create new elements, use the toolbar on the right.
If this is more than you need, check out the <a href="#/simple">simple list demo</a>
</div>
<div class="nestedDemo row" ng-include="'nested/nested.html'"></div>

126
js/vendor/angular-draganddrop/demo/nested/nested.css поставляемый Normal file
Просмотреть файл

@ -0,0 +1,126 @@
/***************************** Dropzone Styling *****************************/
/**
* The dnd-list should always have a min-height,
* otherwise you can't drop to it once it's empty
*/
.nestedDemo .dropzone ul[dnd-list] {
margin: 0px;
min-height: 42px;
padding-left: 0px;
}
.nestedDemo .dropzone li {
background-color: #fff;
border: 1px solid #ddd;
display: block;
padding: 0px;
}
/**
* Reduce opacity of elements during the drag operation. This allows the user
* to see where he is dropping his element, even if the element is huge. The
* .dndDragging class is automatically set during the drag operation.
*/
.nestedDemo .dropzone .dndDragging {
opacity: 0.7;
}
/**
* The dndDraggingSource class will be applied to the source element of a drag
* operation. It makes sense to hide it to give the user the feeling that he's
* actually moving it. Note that the source element has also .dndDragging class.
*/
.nestedDemo .dropzone .dndDraggingSource {
display: none;
}
/**
* An element with .dndPlaceholder class will be added as child of the dnd-list
* while the user is dragging over it.
*/
.nestedDemo .dropzone .dndPlaceholder {
background-color: #ddd;
display: block;
min-height: 42px;
}
/***************************** Element Selection *****************************/
.nestedDemo .dropzone .selected .item {
color: #3c763d;
background-color: #dff0d8;
}
.nestedDemo .dropzone .selected .box {
border-color: #d6e9c6;
}
.nestedDemo .dropzone .selected .box > h3 {
background-color: #dff0d8;
background-image: linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);
border-color: #d6e9c6;
color: #3c763d;
}
/***************************** Element type specific styles *****************************/
.nestedDemo .dropzone .item {
padding: 10px 15px;
}
.nestedDemo .dropzone .container-element {
margin: 10px;
}
.nestedDemo .dropzone .container-element .column {
float: left;
width: 50%;
}
/***************************** Toolbox *****************************/
.nestedDemo .toolbox ul {
cursor: move;
list-style: none;
padding-left: 0px;
}
.nestedDemo .toolbox button {
margin: 5px;
opacity: 1.0;
width: 123px;
}
.nestedDemo .toolbox .dndDragging {
opacity: 0.5;
}
.nestedDemo .toolbox .dndDraggingSource {
opacity: 1.0;
}
/***************************** Trashcan *****************************/
.nestedDemo .trashcan ul {
list-style: none;
padding-left: 0px;
}
.nestedDemo .trashcan img {
width: 100%;
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
filter: grayscale(100%);
}
.nestedDemo .trashcan .dndDragover img {
width: 100%;
-webkit-filter: none;
-moz-filter: none;
filter: none;
}
.nestedDemo .trashcan .dndPlaceholder {
display: none;
}

85
js/vendor/angular-draganddrop/demo/nested/nested.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,85 @@
<!-- Markup for lists inside the dropzone. It's inside a seperate template
because it will be used recursively. The dnd-list directive enables
to drop elements into the referenced array. The dnd-draggable directive
makes an element draggable and will transfer the object that was
assigned to it. If an element was dragged away, you have to remove
it from the original list yourself using the dnd-moved attribute -->
<script type="text/ng-template" id="list.html">
<ul dnd-list="list">
<li ng-repeat="item in list"
dnd-draggable="item"
dnd-effect-allowed="move"
dnd-moved="list.splice($index, 1)"
dnd-selected="models.selected = item"
ng-class="{selected: models.selected === item}"
ng-include="item.type + '.html'">
</li>
</ul>
</script>
<!-- This template is responsible for rendering a container element. It uses
the above list template to render each container column -->
<script type="text/ng-template" id="container.html">
<div class="container-element box box-blue">
<h3>Container {{item.id}}</h3>
<div class="column" ng-repeat="list in item.columns" ng-include="'list.html'"></div>
<div class="clearfix"></div>
</div>
</script>
<!-- Template for a normal list item -->
<script type="text/ng-template" id="item.html">
<div class="item">Item {{item.id}}</div>
</script>
<!-- Main area with dropzones and source code -->
<div class="col-md-10">
<div class="row">
<div ng-repeat="(zone, list) in models.dropzones" class="col-md-6">
<div class="dropzone box box-yellow">
<!-- The dropzone also uses the list template -->
<h3>Dropzone {{zone}}</h3>
<div ng-include="'list.html'"></div>
</div>
</div>
</div>
<div view-source="nested"></div>
<h2>Generated Model</h2>
<pre>{{modelAsJson}}</pre>
</div>
<!-- Sidebar -->
<div class="col-md-2">
<div class="toolbox box box-grey box-padding">
<h3>New Elements</h3>
<ul>
<!-- The toolbox only allows to copy objects, not move it. After a new
element was created, dnd-copied is invoked and we generate the next id -->
<li ng-repeat="item in models.templates"
dnd-draggable="item"
dnd-effect-allowed="copy"
dnd-copied="item.id = item.id + 1"
>
<button type="button" class="btn btn-default btn-lg" disabled="disabled">{{item.type}}</button>
</li>
</ul>
</div>
<div ng-if="models.selected" class="box box-grey box-padding">
<h3>Selected</h3>
<strong>Type: </strong> {{models.selected.type}}<br>
<input type="text" ng-model="models.selected.id" class="form-control" style="margin-top: 5px" />
</div>
<div class="trashcan box box-grey box-padding">
<!-- If you use [] as referenced list, the dropped elements will be lost -->
<h3>Trashcan</h3>
<ul dnd-list="[]">
<li><img src="nested/trashcan.jpg"></li>
</ul>
</div>
</div>

122
js/vendor/angular-draganddrop/demo/nested/nested.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,122 @@
/**
* The controller doesn't do much more than setting the initial data model
*/
angular.module("demo").controller("NestedListsDemoController", function($scope) {
$scope.models = {
selected: null,
templates: [
{type: "item", id: 2},
{type: "container", id: 1, columns: [[], []]}
],
dropzones: {
"A": [
{
"type": "container",
"id": 1,
"columns": [
[
{
"type": "item",
"id": "1"
},
{
"type": "item",
"id": "2"
}
],
[
{
"type": "item",
"id": "3"
}
]
]
},
{
"type": "item",
"id": "4"
},
{
"type": "item",
"id": "5"
},
{
"type": "item",
"id": "6"
}
],
"B": [
{
"type": "item",
"id": 7
},
{
"type": "item",
"id": "8"
},
{
"type": "container",
"id": "2",
"columns": [
[
{
"type": "item",
"id": "9"
},
{
"type": "item",
"id": "10"
},
{
"type": "item",
"id": "11"
}
],
[
{
"type": "item",
"id": "12"
},
{
"type": "container",
"id": "3",
"columns": [
[
{
"type": "item",
"id": "13"
}
],
[
{
"type": "item",
"id": "14"
}
]
]
},
{
"type": "item",
"id": "15"
},
{
"type": "item",
"id": "16"
}
]
]
},
{
"type": "item",
"id": 16
}
]
}
};
$scope.$watch('models.dropzones', function(model) {
$scope.modelAsJson = angular.toJson(model, true);
}, true);
});

Двоичные данные
js/vendor/angular-draganddrop/demo/nested/trashcan.jpg поставляемый Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 28 KiB

38
js/vendor/angular-draganddrop/demo/simple/simple-frame.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,38 @@
<h1>Demo: Simple Lists</h1>
<div class="alert alert-success">
<strong>Instructions:</strong>
Drag &amp; drop the list items to move them around, or just click to select them.
If that's too boring, check out the <a href="#/nested">nested container demo</a>
</div>
<div class="simpleDemo row">
<div class="col-md-8">
<div class="row">
<div ng-repeat="(listName, list) in models.lists" class="col-md-6">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">List {{listName}}</h3>
</div>
<div class="panel-body" ng-include="'simple/simple.html'"></div>
</div>
</div>
</div>
<div view-source="simple"></div>
</div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Generated Model</h3>
</div>
<div class="panel-body">
<pre class="code">{{modelAsJson}}</pre>
</div>
</div>
</div>
</div>

47
js/vendor/angular-draganddrop/demo/simple/simple.css поставляемый Normal file
Просмотреть файл

@ -0,0 +1,47 @@
/**
* The dnd-list should always have a min-height,
* otherwise you can't drop to it once it's empty
*/
.simpleDemo ul[dnd-list] {
min-height: 42px;
padding-left: 0px;
}
/**
* The dndDraggingSource class will be applied to
* the source element of a drag operation. It makes
* sense to hide it to give the user the feeling
* that he's actually moving it.
*/
.simpleDemo ul[dnd-list] .dndDraggingSource {
display: none;
}
/**
* An element with .dndPlaceholder class will be
* added to the dnd-list while the user is dragging
* over it.
*/
.simpleDemo ul[dnd-list] .dndPlaceholder {
background-color: #ddd;
display: block;
min-height: 42px;
}
.simpleDemo ul[dnd-list] li {
background-color: #fff;
border: 1px solid #ddd;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
display: block;
padding: 10px 15px;
margin-bottom: -1px;
}
/**
* Show selected elements in green
*/
.simpleDemo ul[dnd-list] li.selected {
background-color: #dff0d8;
color: #3c763d;
}

17
js/vendor/angular-draganddrop/demo/simple/simple.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
<!-- The dnd-list directive allows to drop elements into it.
The dropped data will be added to the referenced list -->
<ul dnd-list="list">
<!-- The dnd-draggable directive makes an element draggable and will
transfer the object that was assigned to it. If an element was
dragged away, you have to remove it from the original list
yourself using the dnd-moved attribute -->
<li ng-repeat="item in list"
dnd-draggable="item"
dnd-moved="list.splice($index, 1)"
dnd-effect-allowed="move"
dnd-selected="models.selected = item"
ng-class="{'selected': models.selected === item}"
>
{{item.label}}
</li>
</ul>

19
js/vendor/angular-draganddrop/demo/simple/simple.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,19 @@
angular.module("demo").controller("SimpleDemoController", function($scope) {
$scope.models = {
selected: null,
lists: {"A": [], "B": []}
};
// Generate initial model
for (var i = 1; i <= 3; ++i) {
$scope.models.lists.A.push({label: "Item A" + i});
$scope.models.lists.B.push({label: "Item B" + i});
}
// Model to JSON for demo purpose
$scope.$watch('models', function(model) {
$scope.modelAsJson = angular.toJson(model, true);
}, true);
});

40
js/vendor/angular-draganddrop/demo/types/types-frame.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,40 @@
<h1>Demo: Item Types</h1>
<div class="alert alert-success">
<strong>Instructions:</strong>
Drag &amp; drop the names to move them around. Note that the names can not be
dropped in the list for the wrong gender. This is achieved with the dnd-type and
dnd-allowed-types attributes.
This demo also shows the use of the dnd-disable-if attribute, which is used here
to limit the number of names per list, as well as fix Alex' position.
You can combine these functions with <a href="#/nested">nested lists</a>
to build very powerful UIs.
</div>
<div class="typesDemo row">
<div ng-repeat="list in lists" class="col-md-4">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">List of {{list.label}} (max. {{list.max}})</h3>
</div>
<div class="panel-body" ng-include="'types/types.html'"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8" view-source="types"></div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">List Models</h3>
</div>
<div class="panel-body">
<pre class="code">{{modelAsJson}}</pre>
</div>
</div>
</div>
</div>

102
js/vendor/angular-draganddrop/demo/types/types.css поставляемый Normal file
Просмотреть файл

@ -0,0 +1,102 @@
/**
* For the correct positioning of the placeholder element, the dnd-list and
* it's children must have position: relative
*/
.typesDemo ul[dnd-list],
.typesDemo ul[dnd-list] > li {
position: relative;
}
/**
* The dnd-list should always have a min-height,
* otherwise you can't drop to it once it's empty
*/
.typesDemo ul[dnd-list] {
min-height: 42px;
padding-left: 0px;
}
/**
* The dndDraggingSource class will be applied to
* the source element of a drag operation. It makes
* sense to hide it to give the user the feeling
* that he's actually moving it.
*/
.typesDemo ul[dnd-list] .dndDraggingSource {
display: none;
}
/**
* An element with .dndPlaceholder class will be
* added to the dnd-list while the user is dragging
* over it.
*/
.typesDemo ul[dnd-list] .dndPlaceholder {
display: block;
background-color: #ddd;
padding: 10px 15px;
min-height: 42px;
}
/**
* The dnd-lists's child elements currently MUST have
* position: relative. Otherwise we can not determine
* whether the mouse pointer is in the upper or lower
* half of the element we are dragging over. In other
* browsers we can use event.offsetY for this.
*/
.typesDemo ul[dnd-list] li {
background-color: #fff;
border: 1px solid #ddd;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
display: block;
margin-bottom: -1px;
/* Disable text selection if item is not draggable */
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.typesDemo ul[dnd-list] li dnd-nodrag {
display: block;
padding: 10px 15px;
}
/**
* Gender-specifc background
*/
.typesDemo ul[dnd-list] li.background-man {
background-color: #CAE0FC;
}
.typesDemo ul[dnd-list] li.background-woman {
background-color: #FFE2F5;
}
.typesDemo ul[dnd-list] input.background-man {
background-color: #D8E9FF;
color: #2F4D99;
}
.typesDemo ul[dnd-list] input.background-woman {
background-color: #FFF0FA;
color: #D84FA7;
}
/**
* Handle positioning
*/
.typesDemo .handle {
cursor: move;
position: absolute;
top: 14px;
}
.typesDemo .name {
margin-left: 20px;
}

24
js/vendor/angular-draganddrop/demo/types/types.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,24 @@
<ul dnd-list="list.people"
dnd-allowed-types="list.allowedTypes"
dnd-disable-if="list.people.length >= list.max">
<li ng-repeat="person in list.people"
dnd-draggable="person"
dnd-type="person.type"
dnd-disable-if="person.type == 'unknown'"
dnd-moved="list.people.splice($index, 1)"
class="background-{{person.type}}"
>
<dnd-nodrag>
<div dnd-handle class="handle">:::</div>
<div class="name">
<input type="text" ng-model="person.name" class="background-{{person.type}} form-control input-sm">
</div>
</dnd-nodrag>
</li>
<li class="dndPlaceholder">
Drop any <strong>{{list.allowedTypes.join(' or ')}}</strong> here
</li>
</ul>

43
js/vendor/angular-draganddrop/demo/types/types.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,43 @@
angular.module("demo").controller("TypesDemoController", function($scope) {
$scope.lists = [
{
label: "Men",
allowedTypes: ['man'],
max: 4,
people: [
{name: "Bob", type: "man"},
{name: "Charlie", type: "man"},
{name: "Dave", type: "man"}
]
},
{
label: "Women",
allowedTypes: ['woman'],
max: 4,
people: [
{name: "Alice", type: "woman"},
{name: "Eve", type: "woman"},
{name: "Peggy", type: "woman"}
]
},
{
label: "People",
allowedTypes: ['man', 'woman'],
max: 6,
people: [
{name: "Frank", type: "man"},
{name: "Mallory", type: "woman"},
{name: "Alex", type: "unknown"},
{name: "Oscar", type: "man"},
{name: "Wendy", type: "woman"}
]
}
];
// Model to JSON for demo purpose
$scope.$watch('lists', function(lists) {
$scope.modelAsJson = angular.toJson(lists, true);
}, true);
});

21
js/vendor/angular-draganddrop/package.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,21 @@
{
"name": "angular-drag-and-drop-lists",
"main": "angular-drag-and-drop-lists.js",
"version": "2.1.0",
"description": "Angular directives for sorting nested lists using the HTML5 Drag and Drop API",
"repository": "https://github.com/marceljuenemann/angular-drag-and-drop-lists",
"license": "MIT",
"main": "angular-drag-and-drop-lists.js",
"devDependencies": {
"angular": "~1.4.9",
"angular-mocks": "~1.4.9",
"http-server": "~0.6.1",
"minifier": "~0.7.1",
"jasmine-core": "~2.4.1",
"jquery": "~2.2.0"
},
"scripts": {
"minify": "minify angular-drag-and-drop-lists.js",
"start": "http-server -p 8000"
}
}

173
js/vendor/angular-draganddrop/test/dndDraggableSpec.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,173 @@
describe('dndDraggable', function() {
var SIMPLE_HTML = '<div dnd-draggable="{hello: \'world\'}"></div>';
describe('constructor', function() {
it('sets the draggable attribute', function() {
var element = compileAndLink(SIMPLE_HTML);
expect(element.attr('draggable')).toBe('true');
});
it('watches and handles the dnd-disabled-if expression', function() {
var element = compileAndLink('<div dnd-draggable dnd-disable-if="disabled"></div>');
expect(element.attr('draggable')).toBe('true');
element.scope().disabled = true;
element.scope().$digest();
expect(element.attr('draggable')).toBe('false');
element.scope().disabled = false;
element.scope().$digest();
expect(element.attr('draggable')).toBe('true');
});
});
describe('dragstart handler', function() {
var element;
beforeEach(function() {
element = compileAndLink(SIMPLE_HTML);
});
it('calls setData with serialized data', function() {
expect(Dragstart.on(element).data).toEqual({'application/x-dnd': '{"hello":"world"}'});
});
it('includes the dnd-type in the mime type', function() {
element = compileAndLink('<div dnd-draggable="{}" dnd-type="\'foo\'"></div>');
expect(Dragstart.on(element).data).toEqual({'application/x-dnd-foo': '{}'});
});
it('converts the dnd-type to lower case', function() {
element = compileAndLink('<div dnd-draggable="{}" dnd-type="\'Foo\'"></div>');
expect(Dragstart.on(element).data).toEqual({'application/x-dnd-foo': '{}'});
});
it('uses application/json mime type if custom types are not allowed', function() {
element = compileAndLink('<div dnd-draggable="[1]"></div>');
var dragstart = Dragstart.on(element, {allowedMimeTypes: ['Text', 'application/json']});
expect(dragstart.data).toEqual({'application/json': '{"item":[1]}'});
});
it('uses Text mime type in Internet Explorer', function() {
element = compileAndLink('<div dnd-draggable="{}" dnd-type="\'Foo\'"></div>');
var dragstart = Dragstart.on(element, {allowedMimeTypes: ['URL', 'Text']});
expect(dragstart.data).toEqual({
'Text': '{"item":{},"type":"foo"}'
});
});
it('stops propagation', function() {
expect(Dragstart.on(element).propagationStopped).toBe(true);
});
it('sets effectAllowed to move by default', function() {
expect(Dragstart.on(element).effectAllowed).toBe('move');
});
it('sets effectAllowed from dnd-effect-allowed', function() {
element = compileAndLink('<div dnd-draggable dnd-effect-allowed="copyMove"></div>');
expect(Dragstart.on(element).effectAllowed).toBe('copyMove');
});
it('sets effectAllowed to single effect in IE', function() {
element = compileAndLink('<div dnd-draggable dnd-effect-allowed="copyLink"></div>');
expect(Dragstart.on(element, {allowedMimeTypes: ['Text']}).effectAllowed).toBe('copy');
});
it('adds CSS classes to element', inject(function($timeout) {
Dragstart.on(element);
expect(element.hasClass('dndDragging')).toBe(true);
expect(element.hasClass('dndDraggingSource')).toBe(false);
$timeout.flush(0);
expect(element.hasClass('dndDraggingSource')).toBe(true);
}));
it('invokes dnd-dragstart callback', function() {
element = compileAndLink('<div dnd-draggable dnd-dragstart="ev = event"></div>');
Dragstart.on(element);
expect(element.scope().ev).toEqual(jasmine.any(DragEventMock));
});
it('does not start dragging if dnd-disable-if is true', function() {
element = compileAndLink('<div dnd-draggable dnd-disable-if="true"></div>');
var dragstart = Dragstart.on(element);
expect(dragstart.returnValue).toBe(true);
expect(dragstart.defaultPrevented).toBe(false);
expect(dragstart.propagationStopped).toBe(false);
});
it('sets the dragImage if event was triggered on a dnd-handle', function() {
var dragstart = Dragstart.on(element, {allowSetDragImage: true, dndHandle: true});
expect(dragstart.dragImage).toBe(element[0]);
});
});
describe('dragend handler', function() {
var element, dragstart;
beforeEach(function() {
element = compileAndLink(SIMPLE_HTML);
dragstart = Dragstart.on(element);
});
it('stops propagation', function() {
expect(dragstart.dragend(element).propagationStopped).toBe(true);
});
it('removes CSS classes from element', inject(function($timeout) {
$timeout.flush(0);
expect(element.hasClass('dndDragging')).toBe(true);
expect(element.hasClass('dndDraggingSource')).toBe(true);
dragstart.dragend(element);
expect(element.hasClass('dndDragging')).toBe(false);
expect(element.hasClass('dndDraggingSource')).toBe(false);
}));
it('removes dndDraggingSource after a timeout', inject(function($timeout) {
// IE 9 might not flush the $timeout before invoking the dragend handler.
expect(element.hasClass('dndDragging')).toBe(true);
expect(element.hasClass('dndDraggingSource')).toBe(false);
dragstart.dragend(element);
expect(element.hasClass('dndDragging')).toBe(false);
expect(element.hasClass('dndDraggingSource')).toBe(false);
$timeout.flush(0);
expect(element.hasClass('dndDragging')).toBe(false);
expect(element.hasClass('dndDraggingSource')).toBe(false);
}));
var dropEffects = {move: 'moved', copy: 'copied', link: 'linked', none: 'canceled'};
angular.forEach(dropEffects, function(callback, dropEffect) {
it('calls callbacks for dropEffect ' + dropEffect, function() {
var html = '<div dnd-draggable="{}" dnd-effect-allowed="' + dropEffect + '" '
+ 'dnd-dragend="returnedDropEffect = dropEffect" '
+ 'dnd-' + callback + '="returnedEvent = event"></div>';
var element = compileAndLink(html);
var target = compileAndLink('<div dnd-list="[]"></div>');
Dragstart.on(element).dragover(target).drop(target).dragend(element);
expect(element.scope().returnedEvent).toEqual(jasmine.any(DragEventMock));
expect(element.scope().returnedDropEffect).toBe(dropEffect);
});
});
});
describe('click handler', function() {
it('does nothing if dnd-selected is not set', function() {
var element = compileAndLink(SIMPLE_HTML);
var click = new DragEventResult(element, 'click', new DataTransferMock(), {});
expect(click.propagationStopped).toBe(false);
});
it('invokes dnd-selected callback and stops propagation', function() {
var element = compileAndLink('<div dnd-draggable dnd-selected="selected = true"></div>');
var click = new DragEventResult(element, 'click', new DataTransferMock(), {});
expect(click.propagationStopped).toBe(true);
expect(element.scope().selected).toBe(true);
});
});
});

576
js/vendor/angular-draganddrop/test/dndListSpec.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,576 @@
describe('dndList', function() {
it('hides the placeholder element', function() {
var element = compileAndLink('<dnd-list><img class="dndPlaceholder"></dnd-list>');
expect(element.children().length).toBe(0);
});
it('disallows dropping if dnd-disable-if is true', function() {
var source = compileAndLink('<div dnd-draggable="{}"></div>');
var element = compileAndLink('<div dnd-list="[]" dnd-disable-if="disabled"></div>');
element.scope().disabled = true;
forAllHandlers(Dragstart.on(source).dragenter(element), element, verifyDropCancelled);
});
it('allows drop if dnd-disable-if is false', function() {
var source = compileAndLink('<div dnd-draggable="{}"></div>');
var element = compileAndLink('<div dnd-list="[]" dnd-disable-if="disabled"></div>');
forAllHandlers(Dragstart.on(source).dragenter(element), element, verifyDropAccepted);
});
it('disallows dropping from external sources', function() {
var element = compileAndLink('<div dnd-list="[]"></div>');
var dragenter = Dragenter.validExternalOn(element);
forAllHandlers(dragenter, element, verifyDropCancelled);
});
it('allows dropping from external sources if dnd-external-sources is set', function() {
var element = compileAndLink('<div dnd-list="[]" dnd-external-sources="true"></div>');
var dragenter = Dragenter.validExternalOn(element);
forAllHandlers(dragenter, element, verifyDropAccepted);
});
it('disallows drop without valid mime types', function() {
var element = compileAndLink('<div dnd-list="[]" dnd-external-sources="true"></div>');
var dragenter = Dragenter.externalOn(element, {'text/plain': '{}'});
forAllHandlers(dragenter, element, verifyDropCancelled);
});
// Old Internet Explorer versions don't have dataTransfer.types.
it('allows drop if dataTransfer.types is undefined', function() {
var element = compileAndLink('<div dnd-list="[]" dnd-external-sources="true"></div>');
var data = angular.toJson({item: {}, type: 'mytype'});
var dragenter = Dragenter.externalOn(element, {'Text': data}, {undefinedTypes: true});
forAllHandlers(dragenter, element, verifyDropAccepted);
});
it('allows drop if dataTransfer.types contains "Text"', function() {
var element = compileAndLink('<div dnd-list="[]" dnd-external-sources="true"></div>');
var data = angular.toJson({item: {}, type: 'mytype'});
var dragenter = Dragenter.externalOn(element, {'Text': data});
forAllHandlers(dragenter, element, verifyDropAccepted);
});
it('allows drop if dataTransfer.types contains "application/json"', function() {
var element = compileAndLink('<div dnd-list="[]" dnd-external-sources="true"></div>');
var data = angular.toJson({item: {}, type: 'mytype'});
var dragenter = Dragenter.externalOn(element, {'x-pdf': '{}', 'application/json': data});
forAllHandlers(dragenter, element, verifyDropAccepted);
});
it('disallows dropping untyped elements if dnd-allowed-types is set', function() {
var source = compileAndLink('<div dnd-draggable="{}"></div>');
var element = compileAndLink('<div dnd-list="[]" dnd-allowed-types="[\'mytype\']"></div>');
forAllHandlers(Dragstart.on(source).dragenter(element), element, verifyDropCancelled);
});
it('allows dropping typed elements if dnd-allowed-types is not set', function() {
var source = compileAndLink('<div dnd-draggable="{}" dnd-type="\'sometype\'"></div>');
var element = compileAndLink('<div dnd-list="[]"></div>');
forAllHandlers(Dragstart.on(source).dragenter(element), element, verifyDropAccepted);
});
it('disallows dropping elements of the wrong type', function() {
var source = compileAndLink('<div dnd-draggable="{}" dnd-type="\'othertype\'"></div>');
var element = compileAndLink('<div dnd-list="[]" dnd-allowed-types="[\'mytype\']"></div>');
forAllHandlers(Dragstart.on(source).dragenter(element), element, verifyDropCancelled);
});
it('allows dropping elements of the correct type', function() {
var source = compileAndLink('<div dnd-draggable="{}" dnd-type="\'mytype\'"></div>');
var element = compileAndLink('<div dnd-list="[]" dnd-allowed-types="[\'MyType\']"></div>');
forAllHandlers(Dragstart.on(source).dragenter(element), element, verifyDropAccepted);
});
it('disallows dropping elements of the wrong type (test for Edge)', function() {
var source = compileAndLink('<div dnd-draggable="{}" dnd-type="\'othertype\'"></div>');
var element = compileAndLink('<div dnd-list="[]" dnd-allowed-types="[\'mytype\']"></div>');
var dragstart = Dragstart.on(source, {allowedMimeTypes: ['text/plain', 'application/json']});
forAllHandlers(dragstart.dragenter(element), element, verifyDropCancelled);
});
it('allows dropping elements of the correct type (test for Edge)', function() {
var source = compileAndLink('<div dnd-draggable="{}" dnd-type="\'mytype\'"></div>');
var element = compileAndLink('<div dnd-list="[]" dnd-allowed-types="[\'mytype\']"></div>');
var dragstart = Dragstart.on(source, {allowedMimeTypes: ['text/plain', 'application/json']});
forAllHandlers(dragstart.dragenter(element), element, verifyDropAccepted);
});
it('allows dropping external elements if correct type is encoded inside', function() {
var element = compileAndLink('<div dnd-list="[]" dnd-allowed-types="[\'myType\']" ' +
'dnd-external-sources="true"></div>');
var data = angular.toJson({item: {}, type: 'mytype'});
var dragenter = Dragenter.externalOn(element, {'application/json': data});
forAllHandlers(dragenter, element, verifyDropAccepted);
});
describe('dragover handler', function() {
var source, element;
beforeEach(function() {
source = compileAndLink('<div dnd-draggable="{}"></div>');
element = compileAndLink('<div dnd-list="list"></div>');
element.scope().list = [];
});
it('adds dndDragover CSS class', function() {
Dragstart.on(source).dragover(element);
expect(element.hasClass('dndDragover')).toBe(true);
});
it('adds placeholder element', function() {
Dragstart.on(source).dragover(element);
expect(element.children().length).toBe(1);
expect(element.children()[0].tagName).toBe('LI');
});
it('reuses custom placeholder element if it exists', function() {
element = compileAndLink('<dnd-list><img class="dndPlaceholder"></dnd-list>');
Dragstart.on(source).dragover(element);
expect(element.children().length).toBe(1);
expect(element.children()[0].tagName).toBe('IMG');
});
it('invokes dnd-dragover callback', function() {
element = createListWithItemsAndCallbacks();
Dragstart.on(source).dragover(element);
expect(element.scope().dragover.event).toEqual(jasmine.any(DragEventMock));
expect(element.scope().dragover.index).toBe(3);
expect(element.scope().dragover.external).toBe(false);
expect(element.scope().dragover.type).toBeUndefined();
expect(element.scope().dragover.item).toBeUndefined();
});
it('invokes dnd-dragover with correct type', function() {
source = compileAndLink('<div dnd-draggable="{}" dnd-type="\'mytype\'"></div>');
element = createListWithItemsAndCallbacks();
Dragstart.on(source).dragover(element);
expect(element.scope().dragover.type).toBe('mytype');
expect(element.scope().dragover.external).toBe(false);
});
it('invokes dnd-dragover with correct type (test for IE)', function() {
source = compileAndLink('<div dnd-draggable="{}" dnd-type="\'mytype\'"></div>');
element = createListWithItemsAndCallbacks();
Dragstart.on(source, {allowedMimeTypes: ['Text']}).dragover(element);
expect(element.scope().dragover.type).toBe('mytype');
expect(element.scope().dragover.external).toBe(false);
});
it('invokes dnd-dragover with correct type for external drops', function() {
element = createListWithItemsAndCallbacks();
Dragenter.externalOn(element, {'application/x-dnd-mytype': {}}).dragover(element);
expect(element.scope().dragover.type).toBe('mytype');
expect(element.scope().dragover.external).toBe(true);
});
it('invokes dnd-dragover with null type for external drops from IE', function() {
element = createListWithItemsAndCallbacks();
Dragenter.externalOn(element, {'Text': 'unaccessible'}).dragover(element);
expect(element.scope().dragover.type).toBeNull();
expect(element.scope().dragover.external).toBe(true);
});
it('invokes dnd-dragover with undefined callback', function() {
element = createListWithItemsAndCallbacks();
Dragstart.on(source).dragover(element);
expect(element.scope().dragover.callback).toBeUndefined();
});
it('invokes dnd-dragover with callback set on dragstart', function() {
source = compileAndLink('<div dnd-draggable="{}" dnd-callback="a*b"></div>');
source.scope().a = 2;
element = compileAndLink('<ul dnd-list="[]" dnd-dragover="result = callback({b: 3});"></ul>');
Dragstart.on(source).dragover(element);
expect(element.scope().result).toBe(6)
});
it('dnd-dragover callback can cancel the drop', function() {
element = compileAndLink('<div dnd-list="list" dnd-dragover="false"></div>');
verifyDropCancelled(Dragstart.on(source).dragover(element), element);
});
it('allows all external drops with Text mime type', function() {
element = compileAndLink('<div dnd-list="[]" dnd-allowed-types="[\'myType\']" ' +
'dnd-external-sources="true"></div>');
var dragenter = Dragenter.externalOn(element, {'Text': 'unaccessible'});
verifyDropAccepted(dragenter.dragover(element), element);
});
describe('placeholder positioning (vertical)', positioningTests(false, false));
describe('placeholder positioning (horizontal)', positioningTests(true, false));
function positioningTests(horizontal) {
return function() {
var clientYField = 'client' + (horizontal ? 'X' : 'Y');
var heightField = horizontal ? 'width' : 'height';
var topField = horizontal ? 'left' : 'top';
beforeEach(function() {
element = createListWithItemsAndCallbacks(horizontal);
angular.element(document.body).append(element);
if (horizontal) {
element.children().css('float','left');
}
});
afterEach(function() {
element.remove();
});
it('adds actual placeholder element', function() {
var options = {target: element.children()[0]};
options[clientYField] = 1;
Dragstart.on(source).dragover(element, options);
expect(element.scope().dragover.index).toBe(0);
expect(angular.element(element.children()[0]).hasClass('dndPlaceholder')).toBe(true);
});
it('inserts before element if mouse is in first half', function() {
var options = {target: element.children()[1]};
var rect = options.target.getBoundingClientRect();
options[clientYField] = rect[topField] + rect[heightField] / 2 - 1;
Dragstart.on(source).dragover(element, options);
expect(element.scope().dragover.index).toBe(1);
});
it('inserts after element if mouse is in second half', function() {
var options = {target: element.children()[1]};
var rect = options.target.getBoundingClientRect();
options[clientYField] = rect[topField] + rect[heightField] / 2 + 1;
Dragstart.on(source).dragover(element, options);
expect(element.scope().dragover.index).toBe(2);
});
};
}
});
describe('drop handler', function() {
var source, element;
beforeEach(function() {
source = compileAndLink('<div dnd-draggable="{example: \'data\'}"></div>');
element = createListWithItemsAndCallbacks();
});
it('inserts into the list and removes dndDragover class', function() {
var dragover = Dragstart.on(source).dragover(element, {target: element.children()[0]});
expect(element.hasClass("dndDragover")).toBe(true);
dragover.drop(element);
expect(element.scope().list).toEqual([1, {example: 'data'}, 2, 3]);
expect(element.hasClass("dndDragover")).toBe(false);
expect(element.children().length).toBe(3);
});
it('inserts in correct position', function() {
Dragstart.on(source).dragover(element, {target: element.children()[1]}).drop(element);
expect(element.scope().list).toEqual([1, 2, {example: 'data'}, 3]);
expect(element.scope().inserted.index).toBe(2);
});
it('invokes the dnd-inserted callback', function() {
Dragstart.on(source).dragover(element).drop(element);
expect(element.scope().inserted.event).toEqual(jasmine.any(DragEventMock));
expect(element.scope().inserted.index).toBe(3);
expect(element.scope().inserted.external).toBe(false);
expect(element.scope().inserted.type).toBeUndefined();
expect(element.scope().inserted.item).toBe(element.scope().list[3]);
});
it('dnd-drop can transform the object', function() {
var testObject = {transformed: true};
element.scope().dropHandler = function(params) {
expect(params.event).toEqual(jasmine.any(DragEventMock));
expect(params.index).toBe(3);
expect(params.external).toBe(false);
expect(params.type).toBeUndefined();
expect(params.item).toEqual({example: 'data'});
return testObject;
};
Dragstart.on(source).dragover(element).drop(element);
expect(element.scope().list[3]).toBe(testObject);
});
it('dnd-drop can cancel the drop', function() {
element.scope().dropHandler = function() { return false; };
var drop = Dragstart.on(source).dragover(element).drop(element);
expect(element.scope().list).toEqual([1, 2, 3]);
expect(element.scope().inserted).toBeUndefined();
verifyDropCancelled(drop, element, true, 3);
});
it('dnd-drop can take care of inserting the element', function() {
element.scope().dropHandler = function() { return true; };
verifyDropAccepted(Dragstart.on(source).dragover(element).drop(element), element);
expect(element.scope().list).toEqual([1, 2, 3]);
});
it('invokes dnd-drop with undefined callback', function() {
element = createListWithItemsAndCallbacks();
Dragstart.on(source).dragover(element).drop(element);
expect(element.scope().drop.callback).toBeUndefined();
});
it('invokes dnd-drop with callback set on dragstart', function() {
source = compileAndLink('<div dnd-draggable="{}" dnd-callback="a*b"></div>');
source.scope().a = 2;
element = compileAndLink('<ul dnd-list="list" dnd-drop="callback({b: 3});"></ul>');
element.scope().list = [];
Dragstart.on(source).dragover(element).drop(element);
expect(element.scope().list).toEqual([6])
});
it('invokes callbacks with correct type', function() {
source = compileAndLink('<div dnd-draggable="{}" dnd-type="\'mytype\'"></div>');
Dragstart.on(source).dragover(element).drop(element);
expect(element.scope().drop.type).toBe('mytype');
expect(element.scope().drop.external).toBe(false);
expect(element.scope().inserted.type).toBe('mytype');
expect(element.scope().inserted.external).toBe(false);
});
it('invokes callbacks with correct type for Edge', function() {
source = compileAndLink('<div dnd-draggable="{}" dnd-type="\'mytype\'"></div>');
Dragstart.on(source, {allowedMimeTypes: ['application/json']}).dragover(element).drop(element);
expect(element.scope().drop.type).toBe('mytype');
expect(element.scope().drop.external).toBe(false);
expect(element.scope().inserted.type).toBe('mytype');
expect(element.scope().inserted.external).toBe(false);
});
it('invokes callbacks with correct type for external elements', function() {
var dragenter = Dragenter.externalOn(element, {'application/x-dnd-mytype': '{}'});
verifyDropAccepted(dragenter.dragover(element).drop(element), element);
expect(element.scope().drop.type).toBe('mytype');
expect(element.scope().drop.external).toBe(true);
expect(element.scope().inserted.type).toBe('mytype');
expect(element.scope().inserted.external).toBe(true);
});
it('invokes callbacks with correct type for external elements (test for Edge)', function() {
var data = angular.toJson({item: [1, 2, 3], type: 'mytype'});
var dragenter = Dragenter.externalOn(element, {'application/json': data});
verifyDropAccepted(dragenter.dragover(element).drop(element), element);
expect(element.scope().drop.type).toBe('mytype');
expect(element.scope().drop.external).toBe(true);
expect(element.scope().inserted.type).toBe('mytype');
expect(element.scope().inserted.external).toBe(true);
expect(element.scope().inserted.item).toEqual([1, 2, 3]);
});
it('disallows drops with wrong type encoded inside (test for Edge)', function() {
element = compileAndLink('<div dnd-list="[]" dnd-allowed-types="[\'myType\']" ' +
'dnd-external-sources="true"></div>');
var data = angular.toJson({item: [], type: 'othertype'});
var dragenter = Dragenter.externalOn(element, {'application/json': data});
verifyDropCancelled(dragenter.dragover(element).drop(element), element, true);
});
it('cancels drop when JSON is invalid', function() {
var dragenter = Dragenter.externalOn(element, {'application/x-dnd': 'Lorem ipsum'});
verifyDropCancelled(dragenter.dragover(element).drop(element), element, true, 3);
});
});
describe('dragleave handler', function() {
var element, dragover;
beforeEach(function() {
element = createListWithItemsAndCallbacks();
angular.element(document.body).append(element);
dragover = Dragstart.on(compileAndLink('<div dnd-draggable="{}"></div>')).dragover(element);
expect(element.hasClass('dndDragover')).toBe(true);
expect(element.children().length).toBe(4);
});
afterEach(function() {
element.remove();
});
it('removes the placeholder and dndDragover class', function() {
var rect = element[0].getBoundingClientRect();
dragover.dragleave(element, {clientX: rect.left - 2, clientY: rect.top - 2});
expect(element.hasClass('dndDragover')).toBe(false);
expect(element.children().length).toBe(3);
});
it('removes the placeholder and dndDragover if child placeholder is already set', function() {
var rect = element[0].getBoundingClientRect();
dragover.dragleave(element, {clientX: rect.left + 2, clientY: rect.top + 2, phShown: true});
expect(element.hasClass('dndDragover')).toBe(false);
expect(element.children().length).toBe(3);
});
it('sets _dndPhShown if mouse is still inside', function() {
var rect = element[0].getBoundingClientRect();
var result = dragover.dragleave(element, {clientX: rect.left + 2, clientY: rect.top + 2});
expect(element.hasClass('dndDragover')).toBe(true);
expect(element.children().length).toBe(4);
expect(result.dndPhShownSet).toBe(true);
});
});
describe('dropEffect', function() {
// This matrix shows the expected drop effect, given two effectAllowed values.
var ALL = [ 'all', 'move', 'copy', 'link', 'copyLink', 'copyMove', 'linkMove'];
var EXPECTED_MATRIX = {
move: ['move', 'move', 'none', 'none', 'none', 'move', 'move'],
copy: ['copy', 'none', 'copy', 'none', 'copy', 'copy', 'none'],
link: ['link', 'none', 'none', 'link', 'link', 'none', 'link'],
copyLink: ['copy', 'none', 'copy', 'link', 'copy', 'copy', 'link'],
copyMove: ['move', 'move', 'copy', 'none', 'copy', 'move', 'move'],
linkMove: ['move', 'move', 'none', 'link', 'link', 'move', 'move'],
all: ['move', 'move', 'copy', 'link', 'copy', 'move', 'move'],
'': ['move', 'move', 'copy', 'link', 'copy', 'move', 'move'],
};
angular.forEach(ALL, function(sourceEffectAllowed, index) {
angular.forEach(EXPECTED_MATRIX, function(expected, targetEffectAllowed) {
expected = expected[index];
it('is ' + expected + ' for effect-allowed ' + sourceEffectAllowed
+ ' and ' + targetEffectAllowed, function() {
var src = compileAndLink('<div dnd-draggable="{}" dnd-dragend="result = dropEffect" '
+ 'dnd-effect-allowed="' + sourceEffectAllowed + '"></div>');
var target = createListWithItemsAndCallbacks(false, targetEffectAllowed);
expect(Dragstart.on(src).effectAllowed).toBe(sourceEffectAllowed);
if (expected != 'none') {
// Verify dragover.
expect(Dragstart.on(src).dragover(target).dropEffect).toBe(expected);
expect(target.scope().dragover.dropEffect).toBe(expected);
// Verify drop.
expect(Dragstart.on(src).dragover(target).drop(target).dropEffect).toBe(expected);
expect(target.scope().drop.dropEffect).toBe(expected);
// Verify dragend.
Dragstart.on(src).dragover(target).drop(target).dragend(src);
expect(src.scope().result).toBe(expected);
} else {
verifyDropCancelled(Dragstart.on(src).dragover(target), target, false, 3);
verifyDropCancelled(Dragstart.on(src).dragover(target).drop(target), target, true, 3);
Dragstart.on(src).dragend(src);
expect(src.scope().result).toBe('none');
}
});
});
});
// In Safari dataTransfer.effectAllowed is always 'all', ignoring the value set in dragstart.
it('is determined from internal state in Safari', function() {
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="link"></div>');
var target = createListWithItemsAndCallbacks(false, 'copyLink');
var options = {effectAllowed: 'all'};
Dragstart.on(src).dragover(target, options).drop(target, options);
expect(target.scope().dragover.dropEffect).toBe('link');
expect(target.scope().drop.dropEffect).toBe('link');
});
// On MacOS, modifiers automatically limit the effectAllowed passed to dragover and drop.
it('is limited by modifier keys on MacOS', function() {
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="all"></div>');
var target = createListWithItemsAndCallbacks();
Dragstart.on(src).dragover(target, {effectAllowed: 'copyLink'}).drop(target);
expect(target.scope().dragover.dropEffect).toBe('copy');
expect(target.scope().drop.dropEffect).toBe('copy');
});
it('is copy if Ctrl key is pressed', function() {
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="all"></div>');
var target = createListWithItemsAndCallbacks();
Dragstart.on(src).dragover(target, {ctrlKey: true}).drop(target);
expect(target.scope().dragover.dropEffect).toBe('copy');
expect(target.scope().drop.dropEffect).toBe('copy');
});
it('is link if Alt key is pressed', function() {
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="all"></div>');
var target = createListWithItemsAndCallbacks();
Dragstart.on(src).dragover(target, {altKey: true}).drop(target);
expect(target.scope().dragover.dropEffect).toBe('link');
expect(target.scope().drop.dropEffect).toBe('link');
});
it('ignores Ctrl key if copy is not possible', function() {
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="linkMove"></div>');
var target = createListWithItemsAndCallbacks();
Dragstart.on(src).dragover(target, {ctrlKey: true}).drop(target);
expect(target.scope().dragover.dropEffect).toBe('move');
expect(target.scope().drop.dropEffect).toBe('move');
});
it('respects effectAllowed from external drops', function() {
var target = createListWithItemsAndCallbacks();
Dragenter.validExternalOn(target, {effectAllowed: 'copyLink'}).dragover(target).drop(target);
expect(target.scope().dragover.dropEffect).toBe('copy');
expect(target.scope().drop.dropEffect).toBe('copy');
});
it('respects effectAllowed from external drops in IE', function() {
var target = createListWithItemsAndCallbacks();
Dragenter.externalOn(target, {'Text': '{}'}, {effectAllowed: 'copyLink'})
.dragover(target).drop(target);
expect(target.scope().dragover.dropEffect).toBe('move');
expect(target.scope().drop.dropEffect).toBe('move');
});
it('ignores effectAllowed from internal drops in IE', function() {
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="copyLink"></div>');
var target = createListWithItemsAndCallbacks();
Dragstart.on(src, {allowedMimeTypes: ['Text']}).dragover(target, {altKey: true});
expect(target.scope().dragover.dropEffect).toBe('link');
});
it('does not set dropEffect in IE', function() {
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="copyLink"></div>');
var target = createListWithItemsAndCallbacks();
var dragover = Dragstart.on(src, {allowedMimeTypes: ['Text']}).dragover(target);
expect(dragover.dropEffect).toBeUndefined();
});
});
function verifyDropAccepted(result) {
expect(result.defaultPrevented).toBe(true);
if (result.type == 'dragenter') {
expect(result.returnValue).toBeUndefined();
expect(result.propagationStopped).toBe(false);
} else {
expect(result.returnValue).toBe(false);
expect(result.propagationStopped).toBe(true);
}
}
function verifyDropCancelled(result, element, opt_defaultPrevented, opt_children) {
expect(result.returnValue).toBe(true);
expect(result.propagationStopped).toBe(false);
expect(result.defaultPrevented).toBe(opt_defaultPrevented || false);
expect(result.dropEffect).toBeUndefined();
expect(element.hasClass("dndDragover")).toBe(false);
expect(element.children().length).toBe(opt_children || 0);
}
function forAllHandlers(dragenter, element, verify) {
verify(dragenter, element);
var dragover = dragenter.dragover(element);
verify(dragover, element);
var dragover2 = dragover.dragover(element);
verify(dragover2, element);
var drop = dragover2.drop(element);
verify(drop, element);
}
function createListWithItemsAndCallbacks(horizontal, effectAllowed) {
var params = '{event: event, dropEffect: dropEffect, index: index, '
+ 'item: item, external: external, type: type, callback: callback}';
var element = compileAndLink('<ul dnd-list="list" dnd-external-sources="true" ' +
'dnd-horizontal-list="' + (horizontal || 'false') + '" ' +
(effectAllowed ? 'dnd-effect-allowed="' + effectAllowed + '" ' : '') +
'dnd-dragover="dragover = ' + params + '" ' +
'dnd-drop="dropHandler(' + params + ')" ' +
'dnd-inserted="inserted = ' + params + '">' +
'<li>A</li><li>B</li><li>C</li></ul>');
element.scope().dropHandler = function(params) {
element.scope().drop = params;
return params.item;
};
element.scope().list = [1, 2, 3];
return element;
}
});

41
js/vendor/angular-draganddrop/test/dndNodragSpec.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,41 @@
describe('dndNodrag', function() {
var element;
beforeEach(function() {
element = compileAndLink('<div dnd-nodrag></div>');
});
it('sets the draggable attribute', function() {
expect(element.attr('draggable')).toBe('true');
});
it('stops propagation and prevents default for dragstart events', function() {
var dragstart = Dragstart.on(element);
expect(dragstart.propagationStopped).toBe(true);
expect(dragstart.defaultPrevented).toBe(true);
});
it('does not call preventDefault if dataTransfer is already set', function() {
var dragstart = Dragstart.on(element, {presetTypes: ['text/plain']});
expect(dragstart.propagationStopped).toBe(true);
expect(dragstart.defaultPrevented).toBe(false);
});
it('does nothing in dragstart if the event was triggered on a dnd-handle', function() {
var dragstart = Dragstart.on(element, {dndHandle: true});
expect(dragstart.propagationStopped).toBe(false);
expect(dragstart.defaultPrevented).toBe(false);
});
it('stops propagation of dragend events', function() {
var dragend = Dragend.on(element);
expect(dragend.propagationStopped).toBe(true);
expect(dragend.defaultPrevented).toBe(false);
});
it('does nothing in dragend if the event was triggered on a dnd-handle', function() {
var dragend = Dragend.on(element, {dndHandle: true});
expect(dragend.propagationStopped).toBe(false);
expect(dragend.defaultPrevented).toBe(false);
});
});

30
js/vendor/angular-draganddrop/test/index.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Testing angular-drag-and-drop-lists...</title>
<link rel="stylesheet" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
<script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
<script src="../node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
<script src="../node_modules/jquery/dist/jquery.js"></script>
<script src="../node_modules/angular/angular.js"></script>
<script src="../node_modules/angular-mocks/angular-mocks.js"></script>
<script src="../angular-drag-and-drop-lists.js"></script>
<!-- Spec files -->
<script src="mocks.js"></script>
<script src="init.js"></script>
<script src="dndDraggableSpec.js"></script>
<script src="dndListSpec.js"></script>
<script src="dndNodragSpec.js"></script>
</head>
<body>
</body>
</html>

21
js/vendor/angular-draganddrop/test/init.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,21 @@
var $compile,
$rootScope;
beforeEach(module('dndLists'));
beforeEach(inject(function(_$compile_, _$rootScope_){
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
afterEach(function() {
// Reset internal dndState in case dragend was not called.
Dragend.on(compileAndLink('<div dnd-draggable="{}"></div>'));
});
function compileAndLink(html) {
var scope = $rootScope.$new();
var element = $compile(html)(scope);
scope.$digest();
return element;
}

199
js/vendor/angular-draganddrop/test/mocks.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,199 @@
"use strict";
class DataTransferMock {
constructor() { this.$results = {}; }
get dropEffect() { throw "Unexcepted dropEffect getter invocation"; }
set dropEffect(value) { throw "Unexcepted dropEffect setter invocation"; }
get effectAllowed() { throw "Unexcepted effectAllowed getter invocation"; }
set effectAllowed(value) { throw "Unexcepted effectAllowed setter invocation"; }
get types() { throw "Unexcepted types getter invocation"; }
set types(value) { throw "Unexcepted types setter invocation"; }
getData() { throw "Unexcepted getData invocation"; }
setData() { throw "Unexcepted setData invocation"; }
setDragImage() { throw "Unexcepted setDragImage invocation"; }
getResults() { return this.$results; }
}
class DragstartDataTransfer extends DataTransferMock {
constructor(options) {
super();
this.$allowSetDragImage = options.allowSetDragImage || false;
this.$allowedMimeTypes = options.allowedMimeTypes || null;
this.$presetTypes = options.presetTypes || [];
this.$results.data = {};
}
get effectAllowed() { throw "Unexcepted effectAllowed getter invocation"; }
set effectAllowed(value) { this.$results.effectAllowed = value; }
get types() { return this.$presetTypes; }
set types(value) { throw "Unexcepted types setter invocation"; }
setData(format, data) {
if (this.$allowedMimeTypes && !this.$allowedMimeTypes.includes(format)) {
throw "Invalid mime type " + format;
}
this.$results.data[format] = data;
}
setDragImage(img) {
if (!this.$allowSetDragImage) throw "Unexcepted setDragImage invocation";
this.$results.dragImage = img;
}
}
class DropzoneDataTransfer extends DataTransferMock {
constructor(data, options) {
super();
this.$data = data;
this.$dropEffect = options.dropEffect || 'move';
this.$effectAllowed = options.effectAllowed || 'move';
this.$types = options.undefinedTypes ? undefined : Object.keys(data);
}
get dropEffect() { throw "Unexcepted dropEffect getter invocation"; }
set dropEffect(value) { this.$results.dropEffect = value; }
get effectAllowed() { return this.$effectAllowed; }
set effectAllowed(value) { throw "Unexcepted effectAllowed setter invocation"; }
get types() { return this.$types; }
set types(value) { throw "Unexcepted types setter invocation"; }
}
class DropDataTransfer extends DropzoneDataTransfer {
getData(format) { return this.$data[format]; }
}
class DragEventMock {
constructor(type, dataTransfer, options) {
this.$type = type;
this.$dataTransfer = dataTransfer;
this.$options = options;
this.$results = {dataTransfer: dataTransfer.getResults()};
}
get clientX() { return this.$options.clientX || 0; }
get clientY() { return this.$options.clientY || 0; }
get ctrlKey() { return this.$options.ctrlKey || false; }
get altKey() { return this.$options.altKey || false; }
get dataTransfer() { return this.$dataTransfer; }
get originalEvent() { return this; }
get target() { return this.$options.target || undefined; }
get type() { return this.$type; }
get _dndHandle() { return this.$options.dndHandle || undefined; }
get _dndPhShown() { return this.$options.phShown || undefined; }
set _dndPhShown(value) { this.$results.setDndPhShown = value; }
preventDefault() { this.$results.invokedPreventDefault = true; }
stopPropagation() { this.$results.invokedStopPropagation = true; }
getResults() { return this.$results; }
}
class DragEventResult {
constructor(element, type, dataTransfer, opt_eventOptions) {
let handler = $._data($(element).get(0), "events")[type][0].handler;
let event = new DragEventMock(type, dataTransfer, opt_eventOptions || {});
this.$results = event.getResults();
this.$results.returnValue = handler(event);
this.$type = type;
}
get propagationStopped() { return !!this.$results.invokedStopPropagation; }
get defaultPrevented() { return !!this.$results.invokedPreventDefault; }
get dndPhShownSet() { return this.$results.setDndPhShown || false; }
get returnValue() { return this.$results.returnValue; }
get dropEffect() { return this.$results.dataTransfer.dropEffect; }
get type() { return this.$type; }
}
class Dragstart extends DragEventResult {
constructor(element, options) {
super(element, 'dragstart', new DragstartDataTransfer(options), options);
}
get data() { return this.$results.dataTransfer.data; }
get dragImage() { return this.$results.dataTransfer.dragImage; }
get effectAllowed() { return this.$results.dataTransfer.effectAllowed; }
dragenter(element, opt_options) {
var options = $.extend({effectAllowed: this.effectAllowed}, opt_options);
return new Dragenter(element, this.$results.dataTransfer.data, options);
}
dragover(element, opt_options) {
return this.dragenter(element, opt_options).dragover(element);
}
dragend(element) {
return Dragend.on(element);
}
static on(element, opt_options) {
return new Dragstart(element, opt_options || {});
}
}
class Dragend extends DragEventResult {
constructor(element, options) {
super(element, 'dragend', new DataTransferMock(), options);
}
static on(element, opt_options) {
return new Dragend(element, opt_options || {});
}
}
class DropzoneEventResult extends DragEventResult {
constructor(element, type, data, dataTransfer, options) {
options.target = options.target || element[0];
super(element, type, dataTransfer, options);
this.$originalData = $.extend({}, data);
this.$options = options;
}
dragover(element, opt_options) {
return new Dragover(element, this.$originalData, $.extend({}, this.$options, opt_options));
}
}
class Dragenter extends DropzoneEventResult {
constructor(element, data, options) {
super(element, 'dragenter', data, new DropzoneDataTransfer(data, options), options);
}
static externalOn(element, data, opt_options) {
return new Dragenter(element, data, opt_options || {});
}
static validExternalOn(element, opt_options) {
return Dragenter.externalOn(element, {'application/x-dnd': '{"hello":"world"}'}, opt_options);
}
}
class Dragover extends DropzoneEventResult {
constructor(element, data, options) {
super(element, 'dragover', data, new DropzoneDataTransfer(data, options), options);
}
dragleave(element, opt_options) {
return new Dragleave(element, this.$originalData, $.extend({}, this.$options, opt_options));
}
drop(element, opt_options) {
return new Drop(element, this.$originalData, $.extend({}, this.$options, opt_options));
}
}
class Dragleave extends DropzoneEventResult {
constructor(element, data, options) {
super(element, 'dragleave', data, new DataTransferMock(), options);
}
}
class Drop extends DropzoneEventResult {
constructor(element, data, options) {
super(element, 'drop', data, new DropDataTransfer(data, options), options);
}
dragend(element) {
return Dragend.on(element);
}
}

20
js/vendor/angular-route/.bower.json поставляемый
Просмотреть файл

@ -1,20 +0,0 @@
{
"name": "angular-route",
"version": "1.5.5",
"license": "MIT",
"main": "./angular-route.js",
"ignore": [],
"dependencies": {
"angular": "1.5.5"
},
"homepage": "https://github.com/angular/bower-angular-route",
"_release": "1.5.5",
"_resolution": {
"type": "version",
"tag": "v1.5.5",
"commit": "642a2dfc05f869f4a56733e182393536184ed4fa"
},
"_source": "https://github.com/angular/bower-angular-route.git",
"_target": "1.5.5",
"_originalSource": "angular-route"
}

20
js/vendor/angular-sanitize/.bower.json поставляемый
Просмотреть файл

@ -1,20 +0,0 @@
{
"name": "angular-sanitize",
"version": "1.5.5",
"license": "MIT",
"main": "./angular-sanitize.js",
"ignore": [],
"dependencies": {
"angular": "1.5.5"
},
"homepage": "https://github.com/angular/bower-angular-sanitize",
"_release": "1.5.5",
"_resolution": {
"type": "version",
"tag": "v1.5.5",
"commit": "84a69853f2a591b1b9d000984e6219924aaff016"
},
"_source": "https://github.com/angular/bower-angular-sanitize.git",
"_target": "1.5.5",
"_originalSource": "angular-sanitize"
}

41
js/vendor/angular-ui-select/.bower.json поставляемый
Просмотреть файл

@ -1,41 +0,0 @@
{
"name": "angular-ui-select",
"homepage": "https://github.com/angular-ui/ui-select",
"authors": [
"AngularUI"
],
"description": "AngularJS ui-select",
"main": [
"dist/select.js",
"dist/select.css"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"src",
"test",
"gulpfile.js",
"karma.conf.js",
"examples"
],
"dependencies": {
"angular": ">=1.2.18"
},
"devDependencies": {
"jquery": "~1.11",
"angular-sanitize": ">=1.2.18",
"angular-mocks": ">=1.2.18"
},
"version": "0.19.8",
"_release": "0.19.8",
"_resolution": {
"type": "version",
"tag": "v0.19.8",
"commit": "fc6502179847b41f864be042d432eec32e8530c4"
},
"_source": "https://github.com/angular-ui/ui-select.git",
"_target": "v0.19.8",
"_originalSource": "https://github.com/angular-ui/ui-select.git"
}

Просмотреть файл

@ -0,0 +1,56 @@
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.appendToBodyDemo.startToggleTimer()"
ng-disabled="ctrl.appendToBodyDemo.remainingTime">
{{ ctrl.appendToBodyDemo.remainingTime ? 'Toggling in ' + (ctrl.appendToBodyDemo.remainingTime / 1000) + ' seconds' : 'Toggle ui-select presence' }}
</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
<div class="select-box" ng-show="ctrl.appendToBodyDemo.present">
<h3>Bootstrap theme</h3>
<p>Selected: {{ctrl.address.selected.formatted_address}}</p>
<ui-select ng-model="ctrl.address.selected"
theme="bootstrap"
ng-disabled="ctrl.disabled"
reset-search-input="false"
style="width: 600px;"
title="Choose an address"
append-to-body="true">
<ui-select-match placeholder="Enter an address...">{{$select.selected.formatted_address}}</ui-select-match>
<ui-select-choices repeat="address in ctrl.addresses track by $index"
refresh="ctrl.refreshAddresses($select.search)"
refresh-delay="0">
<div ng-bind-html="address.formatted_address | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
<p class="alert alert-info positioned">The select dropdown menu should be displayed above this element.</p>
</div>
<div class="select-box" ng-if="ctrl.appendToBodyDemo.present">
<h3>Select2 theme</h3>
<p>Selected: {{ctrl.person.selected}}</p>
<ui-select ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person" append-to-body="true">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<p class="alert alert-info positioned">The select dropdown menu should be displayed above this element.</p>
</div>
<div class="select-box" ng-if="ctrl.appendToBodyDemo.present">
<h3>Selectize theme</h3>
<p>Selected: {{ctrl.country.selected}}</p>
<ui-select ng-model="ctrl.country.selected" theme="selectize" ng-disabled="ctrl.disabled" style="width: 300px;" title="Choose a country" append-to-body="true">
<ui-select-match placeholder="Select or search a country in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="country in ctrl.countries | filter: $select.search">
<span ng-bind-html="country.name | highlight: $select.search"></span>
<small ng-bind-html="country.code | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
<p class="alert alert-info positioned">The select dropdown menu should be displayed above this element.</p>
</div>

42
js/vendor/angular-ui-select/docs/examples/demo-basic.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,42 @@
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
<h3>Bootstrap theme <small>(remote data source)</small></h3>
<p>Selected: {{ctrl.address.selected.formatted_address}}</p>
<ui-select ng-model="ctrl.address.selected"
theme="bootstrap"
ng-disabled="ctrl.disabled"
reset-search-input="false"
style="width: 600px;"
title="Choose an address">
<ui-select-match placeholder="Enter an address...">{{$select.selected.formatted_address}}</ui-select-match>
<ui-select-choices repeat="address in ctrl.addresses track by $index"
refresh="ctrl.refreshAddresses($select.search)"
refresh-delay="0">
<div ng-bind-html="address.formatted_address | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
<h3>Select2 theme</h3>
<p>Selected: {{ctrl.person.selected}}</p>
<ui-select ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<h3>Selectize theme</h3>
<p>Selected: {{ctrl.country.selected}}</p>
<ui-select ng-model="ctrl.country.selected" theme="selectize" ng-disabled="ctrl.disabled" style="width: 300px;" title="Choose a country">
<ui-select-match placeholder="Select or search a country in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="country in ctrl.countries | filter: $select.search">
<span ng-bind-html="country.name | highlight: $select.search"></span>
<small ng-bind-html="country.code | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>

Просмотреть файл

@ -0,0 +1,21 @@
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
<h3>Select2 theme</h3>
<h4>Single property binding with async data</h4>
<p>Selected: <code> {{ctrl.personAsync.selected | json }}</code></p>
<p>List Count: <code>{{ (ctrl.peopleAsync || []).length }}</code></p>
<ui-select ng-model="ctrl.personAsync.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Single property binding with async data">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name || $select.selected}}</ui-select-match>
<ui-select-choices repeat="person.email as person in ctrl.peopleAsync | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<p><small><em>Data will be populated in 3 secs</em></small></p>

Просмотреть файл

@ -0,0 +1,22 @@
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
<h3>Select2 theme</h3>
<p>Selected: <code>{{ctrl.person.selected}}</code></p>
<ui-select ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person.email as person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<br>
<br>
<br>
<button class="btn btn-primary" ng-click="ctrl.person.selected = 'wladimir@email.com'">Change Model</button>
<p>Runs <code>ctrl.person.selected = 'wladimir@email.com'</code></p>

76
js/vendor/angular-ui-select/docs/examples/demo-bootstrap.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,76 @@
<p>Selected: {{ctrl.person.selected.name}}</p>
<form class="form-horizontal">
<fieldset>
<legend>ui-select inside a Bootstrap form</legend>
<div class="form-group">
<label class="col-sm-3 control-label">Default</label>
<div class="col-sm-6">
<ui-select ng-model="ctrl.person.selected" theme="bootstrap">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<div ng-bind-html="item.name | highlight: $select.search"></div>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Grouped</label>
<div class="col-sm-6">
<ui-select ng-model="ctrl.person.selected" theme="bootstrap">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices group-by="'country'" repeat="item in ctrl.people | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">With a clear button</label>
<div class="col-sm-6">
<div class="input-group">
<ui-select allow-clear ng-model="ctrl.person.selected" theme="bootstrap">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
<span class="input-group-btn">
<button type="button" ng-click="ctrl.person.selected = undefined" class="btn btn-default">
<span class="glyphicon glyphicon-trash"></span>
</button>
</span>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Disabled</label>
<div class="col-sm-6">
<ui-select ng-model="ctrl.person.selected" theme="bootstrap" ng-disabled="true">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<div ng-bind-html="item.name | highlight: $select.search"></div>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
</div>
</div>
</fieldset>
</form>

Просмотреть файл

@ -0,0 +1,71 @@
<p>
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<code>ctrl.disabled {{ctrl.disabled | json}}</code>
</p>
<p>
<button class="btn btn-default btn-xs" ng-click="ctrl.enableSearch()">Enable Search</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disableSearch()">Disable Search</button>
<code>ctrl.searchEnabled {{ ctrl.searchEnabled | json }}</code>
</p>
<p>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
</p>
<h3>Bootstrap theme</h3>
<p>Selected: {{ctrl.person.selected}}</p>
<ui-select ng-model="ctrl.person.selected" theme="bootstrap" search-enabled="ctrl.searchEnabled" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<hr />
<p>Number Selected: {{ctrl.multipleDemo.selectedPeople.length}}</p>
<ui-select multiple ng-model="ctrl.multipleDemo.selectedPeople" theme="bootstrap" search-enabled="ctrl.searchEnabled" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select person...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<h3>Select2 theme</h3>
<p>Selected: {{ctrl.person.selected}}</p>
<ui-select ng-model="ctrl.person.selected" theme="select2" search-enabled="ctrl.searchEnabled" ng-disabled="ctrl.disabled" style="min-width: 300px;">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<hr />
<p>Number Selected: {{ctrl.multipleDemo.selectedPeople.length}}</p>
<ui-select multiple ng-model="ctrl.multipleDemo.selectedPeople" theme="select2" search-enabled="ctrl.searchEnabled" ng-disabled="ctrl.disabled" style="min-width: 300px;">
<ui-select-match placeholder="Select person...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<h3>Selectize theme</h3>
<p>Selected: {{ctrl.country.selected}}</p>
<ui-select ng-model="ctrl.country.selected" theme="selectize" search-enabled="ctrl.searchEnabled" ng-disabled="ctrl.disabled" style="width: 300px;">
<ui-select-match placeholder="Select or search a country in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="country in ctrl.countries | filter: $select.search">
<span ng-bind-html="country.name | highlight: $select.search"></span>
<small ng-bind-html="country.code | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>

Просмотреть файл

@ -0,0 +1,106 @@
<h3>Dropdown Position</h3>
<br>
<br>
<br>
<pre>default value can be changed at <strong class="text-warning">uiSelectConfig</strong></pre>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<p>Always UP</p>
<ui-select ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}" position='up'>
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<p>Always DOWN</p>
<ui-select ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}" position='down'>
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<p>AUTO depending on available space (default)</p>
<ui-select ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}" position='auto'>
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>

Просмотреть файл

@ -0,0 +1,26 @@
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
<h3>Event on Select</h3>
<p>Selected: {{ctrl.person.selected}}</p>
<ui-select ng-model="ctrl.person.selected" theme="select2" on-select="ctrl.onSelectCallback($item, $model)" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person.email as person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<hr />
<h3>Event Result</h3>
<p>Called <code>{{ctrl.counter+0}}</code> times</p>
<p>Parameters</p>
<pre>
$model = {{ ctrl.eventResult.model | json }}
$item = {{ ctrl.eventResult.item | json }}
</pre>

47
js/vendor/angular-ui-select/docs/examples/demo-focus.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,47 @@
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
<h1>Focus</h1>
<p>Regular uiSelect</p>
<ui-select ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<h3>Autofocus</h3>
<p>Use the <code>autofocus</code> attribute to automatically focus ui-select when the page is loaded</p>
<ui-select autofocus ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<h3>Focus on Demand</h3>
<p>Use the <code>focus-on</code> attribute, defining a scope event name (<code>UiSelectDemo1</code>) to listen for, to manually trigger focussing of ui-select.</p>
<ui-select focus-on="UiSelectDemo1" ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<p></p>
<p>
<button class="btn btn-default" ng-click="ctrl.setInputFocus()">Set Focus</button>
(<code>$scope.$broadcast('UiSelectDemo1');</code>)
</p>

42
js/vendor/angular-ui-select/docs/examples/demo-group-by.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,42 @@
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
<h1>Group By</h1>
<p>Selected: {{ctrl.person.selected}}</p>
<h3>Grouped using a string <small><code>group-by="'country'"</code></small></h3>
<ui-select ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices group-by="'country'" repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<h3>Grouped using a function <small><code>group-by="ctrl.someGroupFn"</code></small></h3>
<ui-select ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices group-by="ctrl.someGroupFn" repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<h3>Regular</h3>
<ui-select ng-model="ctrl.person.selected" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>

Просмотреть файл

@ -0,0 +1,26 @@
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
<h1>Group Filtering</h1>
<p>You can filter which groups are displayed by ui-select, by using the <code>group-filter</code> attribute. The provided value should either be an array or function.</p>
<p>Selected: {{country.selected}}</p>
<h3> Filter groups by array <small><code>group-filter="['Z','B','C']"</code></small></h3>
<ui-select ng-model="ctrl.country.selected" theme="select2" ng-disabled="ctrl.disabled" style="width: 300px;" title="Choose a country">
<ui-select-match placeholder="Select or search a country in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices group-by="ctrl.firstLetterGroupFn" group-filter="['Z','B','C']" repeat="country in ctrl.countries | filter: $select.search">
<span ng-bind-html="country.name | highlight: $select.search"></span>
<small ng-bind-html="country.code | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
<h3>Filter groups using a function <small><code>group-filter="ctrl.reverseOrderFilterFn"</code></small></h3>
<ui-select ng-model="ctrl.country.selected" theme="select2" ng-disabled="ctrl.disabled" style="width: 300px;" title="Choose a country">
<ui-select-match placeholder="Select or search a country in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices group-by="ctrl.firstLetterGroupFn" group-filter="ctrl.reverseOrderFilterFn" repeat="country in ctrl.countries | filter: $select.search">
<span ng-bind-html="country.name | highlight: $select.search"></span>
<small ng-bind-html="country.code | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>

Просмотреть файл

@ -0,0 +1,89 @@
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
<h1>Multiple Selection</h1>
<h3>Array of strings</h3>
<ui-select multiple ng-model="ctrl.multipleDemo.colors" theme="bootstrap" ng-disabled="ctrl.disabled" close-on-select="false" style="width: 300px;" title="Choose a color">
<ui-select-match placeholder="Select colors...">{{$item}}</ui-select-match>
<ui-select-choices repeat="color in ctrl.availableColors | filter:$select.search">
{{color}}
</ui-select-choices>
</ui-select>
<p>Selected: {{ctrl.multipleDemo.colors}}</p>
<hr>
<h3>Array of objects (sorting enabled)</h3>
<ui-select multiple ng-model="ctrl.multipleDemo.selectedPeople" theme="bootstrap" ng-disabled="ctrl.disabled" sortable="true" close-on-select="false" style="width: 800px;">
<ui-select-match placeholder="Select person...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<p>Selected: {{ctrl.multipleDemo.selectedPeople}}</p>
<hr>
<h3>Deselect callback with single property binding</h3>
<ui-select multiple ng-model="ctrl.multipleDemo.deSelectedPeople" on-remove="ctrl.removed($item, $model)" theme="bootstrap" ng-disabled="ctrl.disabled" close-on-select="false" style="width: 800px;" title="Choose a person">
<ui-select-match placeholder="Select person...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match>
<ui-select-choices repeat="person.email as person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<p>Last Removed:</p>
<pre>
$item = {{ctrl.lastRemoved.item}}
$model = {{ctrl.lastRemoved.model}}
</pre>
<hr>
<h3>Array of objects with single property binding</h3>
<ui-select multiple ng-model="ctrl.multipleDemo.selectedPeopleSimple" theme="bootstrap" ng-disabled="ctrl.disabled" close-on-select="false" style="width: 800px;" title="Choose a person">
<ui-select-match placeholder="Select person...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match>
<ui-select-choices repeat="person.email as person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<p>Selected: {{ctrl.multipleDemo.selectedPeopleSimple}}</p>
<hr>
<h3>Array of objects (with groupBy)</h3>
<ui-select multiple ng-model="ctrl.multipleDemo.selectedPeopleWithGroupBy" theme="bootstrap" ng-disabled="ctrl.disabled" close-on-select="false" style="width: 800px;" title="Choose a person">
<ui-select-match placeholder="Select person...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match>
<ui-select-choices group-by="ctrl.someGroupFn" repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<p>Selected: {{ctrl.multipleDemo.selectedPeopleWithGroupBy}}</p>
<hr>
<h3>Disabling instead of removing selected items</h3>
<ui-select multiple ng-model="ctrl.multipleDemo.removeSelectIsFalse" theme="bootstrap" ng-disabled="ctrl.disabled" close-on-select="false" style="width: 800px;" title="Choose a person" remove-selected="false">
<ui-select-match placeholder="Select person...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<p>Selected: {{ctrl.multipleDemo.removeSelectIsFalse}}</p>
<div style="height:500px"></div>

Просмотреть файл

@ -0,0 +1,47 @@
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
<h1>Object as Item Source</h1>
<p>uiSelect supports providing choices using the <code>(key, value)</code> format, similar to that used by <code>ng-repeat</code></p>
<h3>Using value for binding</h3>
<p>Selected: <code>{{ctrl.person.selectedValue}}</code></p>
<ui-select ng-model="ctrl.person.selectedValue" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.value.name}}</ui-select-match>
<ui-select-choices repeat="person.value as (key, person) in ctrl.peopleObj | filter: { value: { name: $select.search }}">
<div ng-bind-html="person.value.name | highlight: $select.search"></div>
<small>
email: {{person.value.email}}
age: {{person.value.age}}
</small>
</ui-select-choices>
</ui-select>
<h3>Using single property for binding</h3>
<p>Selected: <code>{{ctrl.person.selectedSingle}}</code></p>
<ui-select ng-model="ctrl.person.selectedSingle" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.value.name}}</ui-select-match>
<ui-select-choices repeat="person.value.name as (key, person) in ctrl.peopleObj | filter: { value: { name: $select.search }}">
<div ng-bind-html="person.value.name | highlight: $select.search"></div>
<small>
email: {{person.value.email}}
age: {{person.value.age}}
</small>
</ui-select-choices>
</ui-select>
<h3>Using key for binding</h3>
<p>Selected: <code>{{ctrl.person.selectedSingleKey}}</code></p>
<ui-select ng-model="ctrl.person.selectedSingleKey" theme="select2" ng-disabled="ctrl.disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.value.name}}</ui-select-match>
<ui-select-choices repeat="person.key as (key, person) in ctrl.peopleObj | filter: { value: { name: $select.search }}">
<div ng-bind-html="person.value.name | highlight: $select.search"></div>
<small>
email: {{person.value.email}}
age: {{person.value.age}}
</small>
</ui-select-choices>
</ui-select>

Просмотреть файл

@ -0,0 +1,91 @@
<p>Selected: {{ctrl.person.selected.name}}</p>
<form class="form-horizontal">
<fieldset>
<legend>ui-select inside a Bootstrap form</legend>
<div class="form-group">
<label class="col-sm-3 control-label">Default</label>
<div class="col-sm-6">
<ui-select ng-model="ctrl.person.selected" theme="select2" class="form-control" title="Choose a person">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<div ng-bind-html="item.name | highlight: $select.search"></div>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Multiple</label>
<div class="col-sm-6">
<ui-select multiple sortable="true" ng-model="ctrl.multipleDemo.selectedPeople" theme="select2" class="form-control" title="Choose a person">
<ui-select-match placeholder="Select or search a person in the list...">{{$item.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<div ng-bind-html="item.name | highlight: $select.search"></div>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Grouped</label>
<div class="col-sm-6">
<ui-select ng-model="ctrl.person.selected" theme="select2" class="form-control" title="Choose a person">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices group-by="'country'" repeat="item in ctrl.people | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">With a clear button</label>
<div class="col-sm-6">
<div class="input-group select2-bootstrap-append">
<ui-select ng-model="ctrl.person.selected" theme="select2" class="form-control" title="Choose a person">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
<span class="input-group-btn">
<button type="button" ng-click="ctrl.person.selected = undefined" class="btn btn-default">
<span class="glyphicon glyphicon-trash"></span>
</button>
</span>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Disabled</label>
<div class="col-sm-6">
<ui-select ng-model="ctrl.person.selected" theme="select2" class="form-control" ng-disabled="true" title="Choose a person">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<div ng-bind-html="item.name | highlight: $select.search"></div>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
</div>
</div>
</fieldset>
</form>

Просмотреть файл

@ -0,0 +1,111 @@
<style>
body {
padding: 15px;
}
.selectize-control {
/* Align Selectize with input-group-btn */
top: 2px;
}
.selectize-control > .selectize-dropdown {
top: 34px;
}
/* Reset right rounded corners, see Bootstrap input-groups.less */
.input-group > .selectize-control > .selectize-input {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
</style>
<p>Selected: {{ctrl.person.selected.name}}</p>
<form class="form-horizontal">
<fieldset>
<legend>ui-select inside a Bootstrap form</legend>
<div class="form-group">
<label class="col-sm-3 control-label">Default</label>
<div class="col-sm-6">
<ui-select ng-model="ctrl.person.selected" theme="selectize" title="Choose a person">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<div ng-bind-html="item.name | highlight: $select.search"></div>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Grouped</label>
<div class="col-sm-6">
<ui-select ng-model="ctrl.person.selected" theme="selectize" title="Choose a person">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices group-by="'country'" repeat="item in ctrl.people | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">With a clear button</label>
<div class="col-sm-6">
<div class="input-group">
<ui-select ng-model="ctrl.person.selected" theme="selectize">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
<span class="input-group-btn">
<button type="button" ng-click="ctrl.person.selected = undefined" class="btn btn-default">
<span class="glyphicon glyphicon-trash"></span>
</button>
</span>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Multiple</label>
<div class="col-sm-6">
<ui-select ng-model="ctrl.person.selected" multiple theme="selectize" title="Choose a person">
<ui-select-match placeholder="Select or search a person in the list...">{{$item.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<div ng-bind-html="item.name | highlight: $select.search"></div>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Disabled</label>
<div class="col-sm-6">
<ui-select ng-model="ctrl.person.selected" theme="selectize" class="form-control" ng-disabled="true" title="Choose a person">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="item in ctrl.people | filter: $select.search">
<div ng-bind-html="item.name | highlight: $select.search"></div>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>
</div>
</div>
</fieldset>
</form>

87
js/vendor/angular-ui-select/docs/examples/demo-tagging.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,87 @@
<button class="btn btn-default btn-xs" ng-click="ctrl.enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="ctrl.clear()">Clear ng-model</button>
<h1>Tagging</h1>
<p>Tagging allows the creation of new items not present in the item list</p>
<h3>Simple String Tags <small>(With Custom Tag Label &amp; Sort Enabled)</small></h3>
<ui-select multiple tagging tagging-label="(custom 'new' label)" ng-model="ctrl.multipleDemo.colors" theme="bootstrap" sortable="true" ng-disabled="ctrl.disabled" style="width: 300px;" title="Choose a color">
<ui-select-match placeholder="Select colors...">{{$item}}</ui-select-match>
<ui-select-choices repeat="color in ctrl.availableColors | filter:$select.search">
{{color}}
</ui-select-choices>
</ui-select>
<p>Selected: {{ctrl.multipleDemo.colors}}</p>
<hr>
<h3>Simple String Tags <small>(Predictive Search Model &amp; No Labels)</small></h3>
<ui-select multiple tagging tagging-label="false" ng-model="ctrl.multipleDemo.colors2" theme="bootstrap" ng-disabled="ctrl.disabled" style="width: 300px;" title="Choose a color">
<ui-select-match placeholder="Select colors...">{{$item}}</ui-select-match>
<ui-select-choices repeat="color in ctrl.availableColors | filter:$select.search">
{{color}}
</ui-select-choices>
</ui-select>
<p>Selected: {{ctrl.multipleDemo.colors2}}</p>
<hr>
<h3>Object Tags <small>(with grouping)</small></h3>
<ui-select multiple tagging="ctrl.tagTransform" ng-model="ctrl.multipleDemo.selectedPeople" theme="bootstrap" ng-disabled="ctrl.disabled" style="width: 800px;" title="Choose a person">
<ui-select-match placeholder="Select person...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}" group-by="'country'">
<div ng-if="person.isTag" ng-bind-html="(person.name | highlight: $select.search) +' (new)'"></div>
<div ng-if="!person.isTag" ng-bind-html="person.name + person.isTag| highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<p>Selected: </p>
<pre>selectedPeople = {{ctrl.multipleDemo.selectedPeople | json}}</pre>
<hr>
<h3>Object Tags with Tokenization <small>("<code>&nbsp;</code>", <code>/</code>, <code>,</code>)</small></h3>
<strong>Note that the Space character can't be used literally, use the keyword SPACE - <code>tagging-tokens="SPACE|,|/"</code></strong>
<ui-select multiple tagging="ctrl.tagTransform" tagging-tokens="SPACE|,|/" ng-model="ctrl.multipleDemo.selectedPeople2" theme="bootstrap" ng-disabled="ctrl.disabled" style="width: 800px;" title="Choose a person">
<ui-select-match placeholder="Select person...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-if="person.isTag" ng-bind-html="person.name + ' ' + $select.taggingLabel | highlight: $select.search"></div>
<div ng-if="!person.isTag" ng-bind-html="person.name| highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<p>Selected: </p>
<pre>selectedPeople2 = {{ctrl.multipleDemo.selectedPeople2 | json}}</pre>
<h3>Tagging in Single Select mode</h3>(NOT WORKING)
<ui-select tagging="ctrl.tagTransform" ng-model="ctrl.person.selected" theme="bootstrap" ng-disabled="ctrl.disabled" style="width: 800px;" title="Choose a person">
<ui-select-match placeholder="Select person...">{{$select.selected.name}} &lt;{{$select.selected.email}}&gt;</ui-select-match>
<ui-select-choices repeat="person in ctrl.people | propsFilter: {name: $select.search, age: $select.search}">
<div ng-if="person.isTag" ng-bind-html="(person.name | highlight: $select.search) +' (new)'"></div>
<div ng-if="!person.isTag" ng-bind-html="person.name + person.isTag| highlight: $select.search"></div>
<small>
email: {{person.email}}
age: <span ng-bind-html="''+person.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
<p>Selected:</p>
<pre>ctrl.person.selected = {{ctrl.person.selected | json }}</pre>
<h3>Tagging in Single select mode, with simple strings</h3>(NOT WORKING)
<ui-select tagging tagging-label="('new')" ng-model="ctrl.singleDemo.color" theme="bootstrap" style="width: 800px;" title="Choose a color">
<ui-select-match placeholder="Select color...">{{$select.selected}}</ui-select-match>
<ui-select-choices repeat="color in ctrl.availableColors | filter: $select.search">
<div ng-bind-html="color | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
<p>Selected: <code>{{ctrl.singleDemo.color}}</code></p>
<div style="height:500px"></div>

2
js/vendor/angular-ui-select/docs/index.html поставляемый
Просмотреть файл

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en" ng-app="ui.select.pages">
<head>
<meta charset="utf-8">

Просмотреть файл

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en" ng-app="demo">
<head>
<meta charset="utf-8">

201
js/vendor/angular-ui-select/gulpfile.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,201 @@
var fs = require('fs');
var del = require('del');
var gulp = require('gulp');
var streamqueue = require('streamqueue');
var karma = require('karma').server;
var $ = require('gulp-load-plugins')();
var runSequence = require('run-sequence');
var conventionalRecommendedBump = require('conventional-recommended-bump');
var titleCase = require('title-case');
var config = {
pkg : JSON.parse(fs.readFileSync('./package.json')),
banner:
'/*!\n' +
' * <%= pkg.name %>\n' +
' * <%= pkg.homepage %>\n' +
' * Version: <%= pkg.version %> - <%= timestamp %>\n' +
' * License: <%= pkg.license %>\n' +
' */\n\n\n'
};
gulp.task('default', ['build','test']);
gulp.task('build', ['scripts', 'styles']);
gulp.task('test', ['build', 'karma']);
gulp.task('watch', ['build','karma-watch'], function() {
gulp.watch(['src/**/*.{js,html}'], ['build']);
});
gulp.task('clean', function(cb) {
del(['dist', 'temp'], cb);
});
gulp.task('scripts', ['clean'], function() {
var buildTemplates = function () {
return gulp.src('src/**/*.html')
.pipe($.minifyHtml({
empty: true,
spare: true,
quotes: true
}))
.pipe($.angularTemplatecache({module: 'ui.select'}));
};
var buildLib = function(){
return gulp.src(['src/common.js','src/*.js'])
.pipe($.plumber({
errorHandler: handleError
}))
.pipe($.concat('select_without_templates.js'))
.pipe($.header('(function () { \n"use strict";\n'))
.pipe($.footer('\n}());'))
.pipe(gulp.dest('temp'))
.pipe($.jshint())
.pipe($.jshint.reporter('jshint-stylish'))
.pipe($.jshint.reporter('fail'));
};
return streamqueue({objectMode: true }, buildLib(), buildTemplates())
.pipe($.plumber({
errorHandler: handleError
}))
.pipe($.concat('select.js'))
.pipe($.header(config.banner, {
timestamp: (new Date()).toISOString(), pkg: config.pkg
}))
.pipe(gulp.dest('dist'))
.pipe($.sourcemaps.init())
.pipe($.uglify({preserveComments: 'some'}))
.pipe($.concat('select.min.js'))
.pipe($.sourcemaps.write('./'))
.pipe(gulp.dest('dist'));
});
gulp.task('styles', ['clean'], function() {
return gulp.src(['src/common.css'], {base: 'src'})
.pipe($.sourcemaps.init())
.pipe($.header(config.banner, {
timestamp: (new Date()).toISOString(), pkg: config.pkg
}))
.pipe($.concat('select.css'))
.pipe(gulp.dest('dist'))
.pipe($.minifyCss())
.pipe($.concat('select.min.css'))
.pipe($.sourcemaps.write('../dist', {debug: true}))
.pipe(gulp.dest('dist'));
});
gulp.task('karma', ['build'], function() {
karma.start({configFile : __dirname +'/karma.conf.js', singleRun: true});
});
gulp.task('karma-watch', ['build'], function() {
karma.start({configFile : __dirname +'/karma.conf.js', singleRun: false});
});
gulp.task('pull', function(done) {
$.git.pull();
done();
});
gulp.task('add', function(done) {
$.git.add();
done();
});
gulp.task('recommendedBump', function(done) {
/**
* Bumping version number and tagging the repository with it.
* Please read http://semver.org/
*
* To bump the version numbers accordingly after you did a patch,
* introduced a feature or made a backwards-incompatible release.
*/
conventionalRecommendedBump({preset: 'angular'}, function(err, importance) {
// Get all the files to bump version in
gulp.src(['./package.json'])
.pipe($.bump({type: importance}))
.pipe(gulp.dest('./'));
done();
});
});
gulp.task('changelog', function() {
return gulp.src('CHANGELOG.md', {buffer: false})
.pipe($.conventionalChangelog({preset: 'angular'}))
.pipe(gulp.dest('./'));
});
gulp.task('push', function(done) {
$.git.push('origin', 'master', {args: '--follow-tags'});
done();
});
gulp.task('commit', function() {
return gulp.src('./')
.pipe($.git.commit('chore(release): bump package version and update changelog', {emitData: true}))
.on('data', function(data) {
console.log(data);
});
});
gulp.task('tag', function() {
return gulp.src('package.json')
.pipe($.tagVersion());
});
gulp.task('bump', function(done) {
runSequence('recommendedBump', 'changelog', 'add', 'commit', 'tag', 'push', done);
});
gulp.task('docs', function (cb) {
runSequence('docs:clean', 'docs:examples', 'docs:assets', 'docs:index', cb);
});
gulp.task('docs:clean', function (cb) {
del(['docs-built'], cb)
});
gulp.task('docs:assets', function () {
gulp.src('./dist/*').pipe(gulp.dest('./docs-built/dist'));
return gulp.src('docs/assets/*').pipe(gulp.dest('./docs-built/assets'));
});
gulp.task('docs:examples', function () {
// Need a way to reset filename list: $.filenames('exampleFiles',{overrideMode:true});
return gulp.src(['docs/examples/*.html'])
.pipe($.header(fs.readFileSync('docs/partials/_header.html')))
.pipe($.footer(fs.readFileSync('docs/partials/_footer.html')))
.pipe($.filenames('exampleFiles'))
.pipe(gulp.dest('./docs-built/'));
});
gulp.task('docs:index', function () {
var exampleFiles = $.filenames.get('exampleFiles');
exampleFiles = exampleFiles.map(function (filename) {
var cleaned = titleCase(filename.replace('demo-', '').replace('.html', ''));
return '<h4><a href="./' + filename + '">' + cleaned + '</a> <plnkr-opener example-path="' + filename + '"></plnkr-opener></h4>';
});
return gulp.src('docs/index.html')
.pipe($.replace('<!-- INSERT EXAMPLES HERE -->', exampleFiles.join("\n")))
.pipe(gulp.dest('./docs-built/'));
});
gulp.task('docs:watch', ['docs'], function() {
gulp.watch(['docs/**/*.{js,html}'], ['docs']);
});
var handleError = function (err) {
console.log(err.toString());
this.emit('end');
};

49
js/vendor/angular-ui-select/karma.conf.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,49 @@
module.exports = function(config) {
config.set({
// Base path, that will be used to resolve files and exclude
basePath: '',
// Frameworks to use
frameworks: ['jasmine'],
// List of files / patterns to load in the browser
files: [
'node_modules/jquery/dist/jquery.js',
'node_modules/angular/angular.js',
'node_modules/angular-sanitize/angular-sanitize.js',
'node_modules/angular-mocks/angular-mocks.js',
'dist/select.js',
'test/helpers.js',
'test/**/*.spec.js'
],
// List of files to exclude
exclude: ['./index.js'],
// Web server port
port: 9876,
// Level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// Enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome'],
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: false
});
};

11
js/vendor/angular-ui-select/src/bootstrap/choices.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,11 @@
<ul class="ui-select-choices ui-select-choices-content ui-select-dropdown dropdown-menu"
ng-show="$select.open && $select.items.length > 0">
<li class="ui-select-choices-group" id="ui-select-choices-{{ $select.generatedId }}" >
<div class="divider" ng-show="$select.isGrouped && $index > 0"></div>
<div ng-show="$select.isGrouped" class="ui-select-choices-group-label dropdown-header" ng-bind="$group.name"></div>
<div ng-attr-id="ui-select-choices-row-{{ $select.generatedId }}-{{$index}}" class="ui-select-choices-row"
ng-class="{active: $select.isActive(this), disabled: $select.isDisabled(this)}" role="option">
<span class="ui-select-choices-row-inner"></span>
</div>
</li>
</ul>

Просмотреть файл

@ -0,0 +1,15 @@
<span class="ui-select-match">
<span ng-repeat="$item in $select.selected track by $index">
<span
class="ui-select-match-item btn btn-default btn-xs"
tabindex="-1"
type="button"
ng-disabled="$select.disabled"
ng-click="$selectMultiple.activeMatchIndex = $index;"
ng-class="{'btn-primary':$selectMultiple.activeMatchIndex === $index, 'select-locked':$select.isLocked(this, $index)}"
ui-select-sort="$select.selected">
<span class="close ui-select-match-close" ng-hide="$select.disabled" ng-click="$selectMultiple.removeChoice($index)">&nbsp;&times;</span>
<span uis-transclude-append></span>
</span>
</span>
</span>

16
js/vendor/angular-ui-select/src/bootstrap/match.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,16 @@
<div class="ui-select-match" ng-hide="$select.open && $select.searchEnabled" ng-disabled="$select.disabled" ng-class="{'btn-default-focus':$select.focus}">
<span tabindex="-1"
class="btn btn-default form-control ui-select-toggle"
aria-label="{{ $select.baseTitle }} activate"
ng-disabled="$select.disabled"
ng-click="$select.activate()"
style="outline: 0;">
<span ng-show="$select.isEmpty()" class="ui-select-placeholder text-muted">{{$select.placeholder}}</span>
<span ng-hide="$select.isEmpty()" class="ui-select-match-text pull-left" ng-class="{'ui-select-allow-clear': $select.allowClear && !$select.isEmpty()}" ng-transclude=""></span>
<i class="caret pull-right" ng-click="$select.toggle($event)"></i>
<a ng-show="$select.allowClear && !$select.isEmpty() && ($select.disabled !== true)" aria-label="{{ $select.baseTitle }} clear" style="margin-right: 10px"
ng-click="$select.clear($event)" class="btn btn-xs btn-link pull-right">
<i class="glyphicon glyphicon-remove" aria-hidden="true"></i>
</a>
</span>
</div>

6
js/vendor/angular-ui-select/src/bootstrap/no-choice.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
<ul class="ui-select-no-choice dropdown-menu"
ng-show="$select.items.length == 0">
<li ng-transclude>
</li>
</ul>

Просмотреть файл

@ -0,0 +1,22 @@
<div class="ui-select-container ui-select-multiple ui-select-bootstrap dropdown form-control" ng-class="{open: $select.open}">
<div>
<div class="ui-select-match"></div>
<input type="search"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
class="ui-select-search input-xs"
placeholder="{{$selectMultiple.getPlaceholder()}}"
ng-disabled="$select.disabled"
ng-click="$select.activate()"
ng-model="$select.search"
role="combobox"
aria-expanded="{{$select.open}}"
aria-label="{{$select.baseTitle}}"
ng-class="{'spinner': $select.refreshing}"
ondrop="return false;">
</div>
<div class="ui-select-choices"></div>
<div class="ui-select-no-choice"></div>
</div>

15
js/vendor/angular-ui-select/src/bootstrap/select.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,15 @@
<div class="ui-select-container ui-select-bootstrap dropdown" ng-class="{open: $select.open}">
<div class="ui-select-match"></div>
<span ng-show="$select.open && $select.refreshing && $select.spinnerEnabled" class="ui-select-refreshing {{$select.spinnerClass}}"></span>
<input type="search" autocomplete="off" tabindex="-1"
aria-expanded="true"
aria-label="{{ $select.baseTitle }}"
aria-owns="ui-select-choices-{{ $select.generatedId }}"
class="form-control ui-select-search"
ng-class="{ 'ui-select-search-hidden' : !$select.searchEnabled }"
placeholder="{{$select.placeholder}}"
ng-model="$select.search"
ng-show="$select.open">
<div class="ui-select-choices"></div>
<div class="ui-select-no-choice"></div>
</div>

354
js/vendor/angular-ui-select/src/common.css поставляемый Normal file
Просмотреть файл

@ -0,0 +1,354 @@
/* Style when highlighting a search. */
.ui-select-highlight {
font-weight: bold;
}
.ui-select-offscreen {
clip: rect(0 0 0 0) !important;
width: 1px !important;
height: 1px !important;
border: 0 !important;
margin: 0 !important;
padding: 0 !important;
overflow: hidden !important;
position: absolute !important;
outline: 0 !important;
left: 0px !important;
top: 0px !important;
}
.ui-select-choices-row:hover {
background-color: #f5f5f5;
}
/* Select2 theme */
/* Mark invalid Select2 */
.ng-dirty.ng-invalid > a.select2-choice {
border-color: #D44950;
}
.select2-result-single {
padding-left: 0;
}
.select2-locked > .select2-search-choice-close{
display:none;
}
.select-locked > .ui-select-match-close{
display:none;
}
body > .select2-container.open {
z-index: 9999; /* The z-index Select2 applies to the select2-drop */
}
/* Handle up direction Select2 */
.ui-select-container[theme="select2"].direction-up .ui-select-match,
.ui-select-container.select2.direction-up .ui-select-match {
border-radius: 4px; /* FIXME hardcoded value :-/ */
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.ui-select-container[theme="select2"].direction-up .ui-select-dropdown,
.ui-select-container.select2.direction-up .ui-select-dropdown {
border-radius: 4px; /* FIXME hardcoded value :-/ */
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
border-top-width: 1px; /* FIXME hardcoded value :-/ */
border-top-style: solid;
box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25);
margin-top: -4px; /* FIXME hardcoded value :-/ */
}
.ui-select-container[theme="select2"].direction-up .ui-select-dropdown .select2-search,
.ui-select-container.select2.direction-up .ui-select-dropdown .select2-search {
margin-top: 4px; /* FIXME hardcoded value :-/ */
}
.ui-select-container[theme="select2"].direction-up.select2-dropdown-open .ui-select-match,
.ui-select-container.select2.direction-up.select2-dropdown-open .ui-select-match {
border-bottom-color: #5897fb;
}
.ui-select-container[theme="select2"] .ui-select-dropdown .ui-select-search-hidden,
.ui-select-container[theme="select2"] .ui-select-dropdown .ui-select-search-hidden input{
opacity: 0;
height: 0;
min-height: 0;
padding: 0;
margin: 0;
border:0;
}
/* Selectize theme */
/* Helper class to show styles when focus */
.selectize-input.selectize-focus{
border-color: #007FBB !important;
}
/* Fix input width for Selectize theme */
.selectize-control.single > .selectize-input > input {
width: 100%;
}
/* Fix line break when there's at least one item selected with the Selectize theme */
.selectize-control.multi > .selectize-input > input {
margin: 0 !important;
}
/* Fix dropdown width for Selectize theme */
.selectize-control > .selectize-dropdown {
width: 100%;
}
/* Mark invalid Selectize */
.ng-dirty.ng-invalid > div.selectize-input {
border-color: #D44950;
}
/* Handle up direction Selectize */
.ui-select-container[theme="selectize"].direction-up .ui-select-dropdown {
box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25);
margin-top: -2px; /* FIXME hardcoded value :-/ */
}
.ui-select-container[theme="selectize"] input.ui-select-search-hidden{
opacity: 0;
height: 0;
min-height: 0;
padding: 0;
margin: 0;
border:0;
width: 0;
}
/* Bootstrap theme */
/* Helper class to show styles when focus */
.btn-default-focus {
color: #333;
background-color: #EBEBEB;
border-color: #ADADAD;
text-decoration: none;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
}
.ui-select-bootstrap .ui-select-toggle {
position: relative;
}
.ui-select-bootstrap .ui-select-toggle > .caret {
position: absolute;
height: 10px;
top: 50%;
right: 10px;
margin-top: -2px;
}
/* Fix Bootstrap dropdown position when inside a input-group */
.input-group > .ui-select-bootstrap.dropdown {
/* Instead of relative */
position: static;
}
.input-group > .ui-select-bootstrap > input.ui-select-search.form-control {
border-radius: 4px; /* FIXME hardcoded value :-/ */
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group > .ui-select-bootstrap > input.ui-select-search.form-control.direction-up {
border-radius: 4px !important; /* FIXME hardcoded value :-/ */
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.ui-select-bootstrap .ui-select-search-hidden{
opacity: 0;
height: 0;
min-height: 0;
padding: 0;
margin: 0;
border:0;
}
.ui-select-bootstrap > .ui-select-match > .btn{
/* Instead of center because of .btn */
text-align: left !important;
}
.ui-select-bootstrap > .ui-select-match > .caret {
position: absolute;
top: 45%;
right: 15px;
}
/* See Scrollable Menu with Bootstrap 3 http://stackoverflow.com/questions/19227496 */
.ui-select-bootstrap > .ui-select-choices ,.ui-select-bootstrap > .ui-select-no-choice {
width: 100%;
height: auto;
max-height: 200px;
overflow-x: hidden;
margin-top: -1px;
}
body > .ui-select-bootstrap.open {
z-index: 1000; /* Standard Bootstrap dropdown z-index */
}
.ui-select-multiple.ui-select-bootstrap {
height: auto;
padding: 3px 3px 0 3px;
}
.ui-select-multiple.ui-select-bootstrap input.ui-select-search {
background-color: transparent !important; /* To prevent double background when disabled */
border: none;
outline: none;
height: 1.666666em;
margin-bottom: 3px;
}
.ui-select-multiple.ui-select-bootstrap .ui-select-match .close {
font-size: 1.6em;
line-height: 0.75;
}
.ui-select-multiple.ui-select-bootstrap .ui-select-match-item {
outline: 0;
margin: 0 3px 3px 0;
}
.ui-select-multiple .ui-select-match-item {
position: relative;
}
.ui-select-multiple .ui-select-match-item.dropping .ui-select-match-close {
pointer-events: none;
}
.ui-select-multiple:hover .ui-select-match-item.dropping-before:before {
content: "";
position: absolute;
top: 0;
right: 100%;
height: 100%;
margin-right: 2px;
border-left: 1px solid #428bca;
}
.ui-select-multiple:hover .ui-select-match-item.dropping-after:after {
content: "";
position: absolute;
top: 0;
left: 100%;
height: 100%;
margin-left: 2px;
border-right: 1px solid #428bca;
}
.ui-select-bootstrap .ui-select-choices-row>span {
cursor: pointer;
display: block;
padding: 3px 20px;
clear: both;
font-weight: 400;
line-height: 1.42857143;
color: #333;
white-space: nowrap;
}
.ui-select-bootstrap .ui-select-choices-row>span:hover, .ui-select-bootstrap .ui-select-choices-row>span:focus {
text-decoration: none;
color: #262626;
background-color: #f5f5f5;
}
.ui-select-bootstrap .ui-select-choices-row.active>span {
color: #fff;
text-decoration: none;
outline: 0;
background-color: #428bca;
}
.ui-select-bootstrap .ui-select-choices-row.disabled>span,
.ui-select-bootstrap .ui-select-choices-row.active.disabled>span {
color: #777;
cursor: not-allowed;
background-color: #fff;
}
/* fix hide/show angular animation */
.ui-select-match.ng-hide-add,
.ui-select-search.ng-hide-add {
display: none !important;
}
/* Mark invalid Bootstrap */
.ui-select-bootstrap.ng-dirty.ng-invalid > button.btn.ui-select-match {
border-color: #D44950;
}
/* Handle up direction Bootstrap */
.ui-select-container[theme="bootstrap"].direction-up .ui-select-dropdown {
box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25);
}
.ui-select-bootstrap .ui-select-match-text {
width: 100%;
padding-right: 1em;
}
.ui-select-bootstrap .ui-select-match-text span {
display: inline-block;
width: 100%;
overflow: hidden;
}
.ui-select-bootstrap .ui-select-toggle > a.btn {
position: absolute;
height: 10px;
right: 10px;
margin-top: -2px;
}
/* Spinner */
.ui-select-refreshing.glyphicon {
position: absolute;
right: 0;
padding: 8px 27px;
}
@-webkit-keyframes ui-select-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes ui-select-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
.ui-select-spin {
-webkit-animation: ui-select-spin 2s infinite linear;
animation: ui-select-spin 2s infinite linear;
}
.ui-select-refreshing.ng-animate {
-webkit-animation: none 0s;
}

175
js/vendor/angular-ui-select/src/common.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,175 @@
var KEY = {
TAB: 9,
ENTER: 13,
ESC: 27,
SPACE: 32,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
SHIFT: 16,
CTRL: 17,
ALT: 18,
PAGE_UP: 33,
PAGE_DOWN: 34,
HOME: 36,
END: 35,
BACKSPACE: 8,
DELETE: 46,
COMMAND: 91,
MAP: { 91 : "COMMAND", 8 : "BACKSPACE" , 9 : "TAB" , 13 : "ENTER" , 16 : "SHIFT" , 17 : "CTRL" , 18 : "ALT" , 19 : "PAUSEBREAK" , 20 : "CAPSLOCK" , 27 : "ESC" , 32 : "SPACE" , 33 : "PAGE_UP", 34 : "PAGE_DOWN" , 35 : "END" , 36 : "HOME" , 37 : "LEFT" , 38 : "UP" , 39 : "RIGHT" , 40 : "DOWN" , 43 : "+" , 44 : "PRINTSCREEN" , 45 : "INSERT" , 46 : "DELETE", 48 : "0" , 49 : "1" , 50 : "2" , 51 : "3" , 52 : "4" , 53 : "5" , 54 : "6" , 55 : "7" , 56 : "8" , 57 : "9" , 59 : ";", 61 : "=" , 65 : "A" , 66 : "B" , 67 : "C" , 68 : "D" , 69 : "E" , 70 : "F" , 71 : "G" , 72 : "H" , 73 : "I" , 74 : "J" , 75 : "K" , 76 : "L", 77 : "M" , 78 : "N" , 79 : "O" , 80 : "P" , 81 : "Q" , 82 : "R" , 83 : "S" , 84 : "T" , 85 : "U" , 86 : "V" , 87 : "W" , 88 : "X" , 89 : "Y" , 90 : "Z", 96 : "0" , 97 : "1" , 98 : "2" , 99 : "3" , 100 : "4" , 101 : "5" , 102 : "6" , 103 : "7" , 104 : "8" , 105 : "9", 106 : "*" , 107 : "+" , 109 : "-" , 110 : "." , 111 : "/", 112 : "F1" , 113 : "F2" , 114 : "F3" , 115 : "F4" , 116 : "F5" , 117 : "F6" , 118 : "F7" , 119 : "F8" , 120 : "F9" , 121 : "F10" , 122 : "F11" , 123 : "F12", 144 : "NUMLOCK" , 145 : "SCROLLLOCK" , 186 : ";" , 187 : "=" , 188 : "," , 189 : "-" , 190 : "." , 191 : "/" , 192 : "`" , 219 : "[" , 220 : "\\" , 221 : "]" , 222 : "'"
},
isControl: function (e) {
var k = e.which;
switch (k) {
case KEY.COMMAND:
case KEY.SHIFT:
case KEY.CTRL:
case KEY.ALT:
return true;
}
if (e.metaKey || e.ctrlKey || e.altKey) return true;
return false;
},
isFunctionKey: function (k) {
k = k.which ? k.which : k;
return k >= 112 && k <= 123;
},
isVerticalMovement: function (k){
return ~[KEY.UP, KEY.DOWN].indexOf(k);
},
isHorizontalMovement: function (k){
return ~[KEY.LEFT,KEY.RIGHT,KEY.BACKSPACE,KEY.DELETE].indexOf(k);
},
toSeparator: function (k) {
var sep = {ENTER:"\n",TAB:"\t",SPACE:" "}[k];
if (sep) return sep;
// return undefined for special keys other than enter, tab or space.
// no way to use them to cut strings.
return KEY[k] ? undefined : k;
}
};
function isNil(value) {
return angular.isUndefined(value) || value === null;
}
/**
* Add querySelectorAll() to jqLite.
*
* jqLite find() is limited to lookups by tag name.
* TODO This will change with future versions of AngularJS, to be removed when this happens
*
* See jqLite.find - why not use querySelectorAll? https://github.com/angular/angular.js/issues/3586
* See feat(jqLite): use querySelectorAll instead of getElementsByTagName in jqLite.find https://github.com/angular/angular.js/pull/3598
*/
if (angular.element.prototype.querySelectorAll === undefined) {
angular.element.prototype.querySelectorAll = function(selector) {
return angular.element(this[0].querySelectorAll(selector));
};
}
/**
* Add closest() to jqLite.
*/
if (angular.element.prototype.closest === undefined) {
angular.element.prototype.closest = function( selector) {
var elem = this[0];
var matchesSelector = elem.matches || elem.webkitMatchesSelector || elem.mozMatchesSelector || elem.msMatchesSelector;
while (elem) {
if (matchesSelector.bind(elem)(selector)) {
return elem;
} else {
elem = elem.parentElement;
}
}
return false;
};
}
var latestId = 0;
var uis = angular.module('ui.select', [])
.constant('uiSelectConfig', {
theme: 'bootstrap',
searchEnabled: true,
sortable: false,
placeholder: '', // Empty by default, like HTML tag <select>
refreshDelay: 1000, // In milliseconds
closeOnSelect: true,
skipFocusser: false,
dropdownPosition: 'auto',
removeSelected: true,
resetSearchInput: true,
generateId: function() {
return latestId++;
},
appendToBody: false,
spinnerEnabled: false,
spinnerClass: 'glyphicon glyphicon-refresh ui-select-spin',
backspaceReset: true
})
// See Rename minErr and make it accessible from outside https://github.com/angular/angular.js/issues/6913
.service('uiSelectMinErr', function() {
var minErr = angular.$$minErr('ui.select');
return function() {
var error = minErr.apply(this, arguments);
var message = error.message.replace(new RegExp('\nhttp://errors.angularjs.org/.*'), '');
return new Error(message);
};
})
// Recreates old behavior of ng-transclude. Used internally.
.directive('uisTranscludeAppend', function () {
return {
link: function (scope, element, attrs, ctrl, transclude) {
transclude(scope, function (clone) {
element.append(clone);
});
}
};
})
/**
* Highlights text that matches $select.search.
*
* Taken from AngularUI Bootstrap Typeahead
* See https://github.com/angular-ui/bootstrap/blob/0.10.0/src/typeahead/typeahead.js#L340
*/
.filter('highlight', function() {
function escapeRegexp(queryToEscape) {
return ('' + queryToEscape).replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
}
return function(matchItem, query) {
return query && matchItem ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<span class="ui-select-highlight">$&</span>') : matchItem;
};
})
/**
* A read-only equivalent of jQuery's offset function: http://api.jquery.com/offset/
*
* Taken from AngularUI Bootstrap Position:
* See https://github.com/angular-ui/bootstrap/blob/master/src/position/position.js#L70
*/
.factory('uisOffset',
['$document', '$window',
function ($document, $window) {
return function(element) {
var boundingClientRect = element[0].getBoundingClientRect();
return {
width: boundingClientRect.width || element.prop('offsetWidth'),
height: boundingClientRect.height || element.prop('offsetHeight'),
top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
};
};
}]);

11
js/vendor/angular-ui-select/src/select2/choices.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,11 @@
<ul tabindex="-1" class="ui-select-choices ui-select-choices-content select2-results">
<li class="ui-select-choices-group" ng-class="{'select2-result-with-children': $select.choiceGrouped($group) }">
<div ng-show="$select.choiceGrouped($group)" class="ui-select-choices-group-label select2-result-label" ng-bind="$group.name"></div>
<ul
id="ui-select-choices-{{ $select.generatedId }}" ng-class="{'select2-result-sub': $select.choiceGrouped($group), 'select2-result-single': !$select.choiceGrouped($group) }">
<li role="option" ng-attr-id="ui-select-choices-row-{{ $select.generatedId }}-{{$index}}" class="ui-select-choices-row" ng-class="{'select2-highlighted': $select.isActive(this), 'select2-disabled': $select.isDisabled(this)}">
<div class="select2-result-label ui-select-choices-row-inner"></div>
</li>
</ul>
</li>
</ul>

13
js/vendor/angular-ui-select/src/select2/match-multiple.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,13 @@
<!--
select2-choice needs to be before ui-select-multiple
otherwise CSS rules from https://github.com/fk/select2-bootstrap-css
do not work: [class^="select2-choice"]
-->
<span class="ui-select-match">
<li class="ui-select-match-item select2-search-choice" ng-repeat="$item in $select.selected track by $index"
ng-class="{'select2-search-choice-focus':$selectMultiple.activeMatchIndex === $index, 'select2-locked':$select.isLocked(this, $index)}"
ui-select-sort="$select.selected">
<span uis-transclude-append></span>
<a href="javascript:;" class="ui-select-match-close select2-search-choice-close" ng-click="$selectMultiple.removeChoice($index)" tabindex="-1"></a>
</li>
</span>

13
js/vendor/angular-ui-select/src/select2/match.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,13 @@
<!--
select2-choice needs to be before ui-select-match
otherwise CSS rules from https://github.com/fk/select2-bootstrap-css
do not work: [class^="select2-choice"]
-->
<a class="select2-choice ui-select-match"
ng-class="{'select2-default': $select.isEmpty()}"
ng-click="$select.toggle($event)" aria-label="{{ $select.baseTitle }} select">
<span ng-show="$select.isEmpty()" class="select2-chosen">{{$select.placeholder}}</span>
<span ng-hide="$select.isEmpty()" class="select2-chosen" ng-transclude></span>
<abbr ng-if="$select.allowClear && !$select.isEmpty()" class="select2-search-choice-close" ng-click="$select.clear($event)"></abbr>
<span class="select2-arrow ui-select-toggle"><b></b></span>
</a>

6
js/vendor/angular-ui-select/src/select2/no-choice.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
<div class="ui-select-no-choice dropdown"
ng-show="$select.items.length == 0">
<div class="dropdown-content">
<div data-selectable="" ng-transclude></div>
</div>
</div>

Просмотреть файл

@ -0,0 +1,32 @@
<div class="ui-select-container ui-select-multiple select2 select2-container select2-container-multi"
ng-class="{'select2-container-active select2-dropdown-open open': $select.open,
'select2-container-disabled': $select.disabled}">
<ul class="select2-choices">
<span class="ui-select-match"></span>
<li class="select2-search-field">
<input
type="search"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
role="combobox"
aria-expanded="true"
aria-owns="ui-select-choices-{{ $select.generatedId }}"
aria-label="{{ $select.baseTitle }}"
aria-activedescendant="ui-select-choices-row-{{ $select.generatedId }}-{{ $select.activeIndex }}"
class="select2-input ui-select-search"
placeholder="{{$selectMultiple.getPlaceholder()}}"
ng-disabled="$select.disabled"
ng-hide="$select.disabled"
ng-model="$select.search"
ng-click="$select.activate()"
style="width: 34px;"
ondrop="return false;">
</li>
</ul>
<div class="ui-select-dropdown select2-drop select2-with-searchbox select2-drop-active"
ng-class="{'select2-display-none': !$select.open || $select.items.length === 0}">
<div class="ui-select-choices"></div>
</div>
</div>

22
js/vendor/angular-ui-select/src/select2/select.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,22 @@
<div class="ui-select-container select2 select2-container"
ng-class="{'select2-container-active select2-dropdown-open open': $select.open,
'select2-container-disabled': $select.disabled,
'select2-container-active': $select.focus,
'select2-allowclear': $select.allowClear && !$select.isEmpty()}">
<div class="ui-select-match"></div>
<div class="ui-select-dropdown select2-drop select2-with-searchbox select2-drop-active"
ng-class="{'select2-display-none': !$select.open}">
<div class="search-container" ng-class="{'ui-select-search-hidden':!$select.searchEnabled, 'select2-search':$select.searchEnabled}">
<input type="search" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
ng-class="{'select2-active': $select.refreshing}"
role="combobox"
aria-expanded="true"
aria-owns="ui-select-choices-{{ $select.generatedId }}"
aria-label="{{ $select.baseTitle }}"
class="ui-select-search select2-input"
ng-model="$select.search">
</div>
<div class="ui-select-choices"></div>
<div class="ui-select-no-choice"></div>
</div>
</div>

11
js/vendor/angular-ui-select/src/selectize/choices.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,11 @@
<div ng-show="$select.open" class="ui-select-choices ui-select-dropdown selectize-dropdown"
ng-class="{'single': !$select.multiple, 'multi': $select.multiple}">
<div class="ui-select-choices-content selectize-dropdown-content">
<div class="ui-select-choices-group optgroup">
<div ng-show="$select.isGrouped" class="ui-select-choices-group-label optgroup-header" ng-bind="$group.name"></div>
<div role="option" class="ui-select-choices-row" ng-class="{active: $select.isActive(this), disabled: $select.isDisabled(this)}">
<div class="option ui-select-choices-row-inner" data-selectable></div>
</div>
</div>
</div>
</div>

Просмотреть файл

@ -0,0 +1,11 @@
<div class="ui-select-match" data-value
ng-repeat="$item in $select.selected track by $index"
ng-click="$selectMultiple.activeMatchIndex = $index;"
ng-class="{'active':$selectMultiple.activeMatchIndex === $index}"
ui-select-sort="$select.selected">
<span class="ui-select-match-item"
ng-class="{'select-locked':$select.isLocked(this, $index)}">
<span uis-transclude-append></span>
<span class="remove ui-select-match-close" ng-hide="$select.disabled" ng-click="$selectMultiple.removeChoice($index)">&times;</span>
</span>
</div>

6
js/vendor/angular-ui-select/src/selectize/match.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
<div ng-hide="$select.searchEnabled && ($select.open || $select.isEmpty())" class="ui-select-match">
<span ng-show="!$select.searchEnabled && ($select.isEmpty() || $select.open)" class="ui-select-placeholder text-muted">{{$select.placeholder}}</span>
<span ng-hide="$select.isEmpty() || $select.open" ng-transclude></span>
</div>

6
js/vendor/angular-ui-select/src/selectize/no-choice.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
<div class="ui-select-no-choice selectize-dropdown"
ng-show="$select.items.length == 0">
<div class="selectize-dropdown-content">
<div data-selectable="" ng-transclude></div>
</div>
</div>

Просмотреть файл

@ -0,0 +1,18 @@
<div class="ui-select-container selectize-control multi plugin-remove_button" ng-class="{'open': $select.open}">
<div class="selectize-input"
ng-class="{'focus': $select.open, 'disabled': $select.disabled, 'selectize-focus' : $select.focus}"
ng-click="$select.open && !$select.searchEnabled ? $select.toggle($event) : $select.activate()">
<div class="ui-select-match"></div>
<input type="search" autocomplete="off" tabindex="-1"
class="ui-select-search"
ng-class="{'ui-select-search-hidden':!$select.searchEnabled}"
placeholder="{{$selectMultiple.getPlaceholder()}}"
ng-model="$select.search"
ng-disabled="$select.disabled"
aria-expanded="{{$select.open}}"
aria-label="{{ $select.baseTitle }}"
ondrop="return false;">
</div>
<div class="ui-select-choices"></div>
<div class="ui-select-no-choice"></div>
</div>

18
js/vendor/angular-ui-select/src/selectize/select.tpl.html поставляемый Normal file
Просмотреть файл

@ -0,0 +1,18 @@
<div class="ui-select-container selectize-control single" ng-class="{'open': $select.open}">
<div class="selectize-input"
ng-class="{'focus': $select.open, 'disabled': $select.disabled, 'selectize-focus' : $select.focus}"
ng-click="$select.open && !$select.searchEnabled ? $select.toggle($event) : $select.activate()">
<div class="ui-select-match"></div>
<input type="search" autocomplete="off" tabindex="-1"
class="ui-select-search ui-select-toggle"
ng-class="{'ui-select-search-hidden':!$select.searchEnabled}"
ng-click="$select.toggle($event)"
placeholder="{{$select.placeholder}}"
ng-model="$select.search"
ng-hide="!$select.isEmpty() && !$select.open"
ng-disabled="$select.disabled"
aria-label="{{ $select.baseTitle }}">
</div>
<div class="ui-select-choices"></div>
<div class="ui-select-no-choice"></div>
</div>

90
js/vendor/angular-ui-select/src/uiSelectChoicesDirective.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,90 @@
uis.directive('uiSelectChoices',
['uiSelectConfig', 'uisRepeatParser', 'uiSelectMinErr', '$compile', '$window',
function(uiSelectConfig, RepeatParser, uiSelectMinErr, $compile, $window) {
return {
restrict: 'EA',
require: '^uiSelect',
replace: true,
transclude: true,
templateUrl: function(tElement) {
// Needed so the uiSelect can detect the transcluded content
tElement.addClass('ui-select-choices');
// Gets theme attribute from parent (ui-select)
var theme = tElement.parent().attr('theme') || uiSelectConfig.theme;
return theme + '/choices.tpl.html';
},
compile: function(tElement, tAttrs) {
if (!tAttrs.repeat) throw uiSelectMinErr('repeat', "Expected 'repeat' expression.");
// var repeat = RepeatParser.parse(attrs.repeat);
var groupByExp = tAttrs.groupBy;
var groupFilterExp = tAttrs.groupFilter;
if (groupByExp) {
var groups = tElement.querySelectorAll('.ui-select-choices-group');
if (groups.length !== 1) throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-group but got '{0}'.", groups.length);
groups.attr('ng-repeat', RepeatParser.getGroupNgRepeatExpression());
}
var parserResult = RepeatParser.parse(tAttrs.repeat);
var choices = tElement.querySelectorAll('.ui-select-choices-row');
if (choices.length !== 1) {
throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row but got '{0}'.", choices.length);
}
choices.attr('ng-repeat', parserResult.repeatExpression(groupByExp))
.attr('ng-if', '$select.open'); //Prevent unnecessary watches when dropdown is closed
var rowsInner = tElement.querySelectorAll('.ui-select-choices-row-inner');
if (rowsInner.length !== 1) {
throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row-inner but got '{0}'.", rowsInner.length);
}
rowsInner.attr('uis-transclude-append', ''); //Adding uisTranscludeAppend directive to row element after choices element has ngRepeat
// If IE8 then need to target rowsInner to apply the ng-click attr as choices will not capture the event.
var clickTarget = $window.document.addEventListener ? choices : rowsInner;
clickTarget.attr('ng-click', '$select.select(' + parserResult.itemName + ',$select.skipFocusser,$event)');
return function link(scope, element, attrs, $select) {
$select.parseRepeatAttr(attrs.repeat, groupByExp, groupFilterExp); //Result ready at $select.parserResult
$select.disableChoiceExpression = attrs.uiDisableChoice;
$select.onHighlightCallback = attrs.onHighlight;
$select.minimumInputLength = parseInt(attrs.minimumInputLength) || 0;
$select.dropdownPosition = attrs.position ? attrs.position.toLowerCase() : uiSelectConfig.dropdownPosition;
scope.$watch('$select.search', function(newValue) {
if(newValue && !$select.open && $select.multiple) $select.activate(false, true);
$select.activeIndex = $select.tagging.isActivated ? -1 : 0;
if (!attrs.minimumInputLength || $select.search.length >= attrs.minimumInputLength) {
$select.refresh(attrs.refresh);
} else {
$select.items = [];
}
});
attrs.$observe('refreshDelay', function() {
// $eval() is needed otherwise we get a string instead of a number
var refreshDelay = scope.$eval(attrs.refreshDelay);
$select.refreshDelay = refreshDelay !== undefined ? refreshDelay : uiSelectConfig.refreshDelay;
});
scope.$watch('$select.open', function(open) {
if (open) {
tElement.attr('role', 'listbox');
$select.refresh(attrs.refresh);
} else {
element.removeAttr('role');
}
});
};
}
};
}]);

756
js/vendor/angular-ui-select/src/uiSelectController.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,756 @@
/**
* Contains ui-select "intelligence".
*
* The goal is to limit dependency on the DOM whenever possible and
* put as much logic in the controller (instead of the link functions) as possible so it can be easily tested.
*/
uis.controller('uiSelectCtrl',
['$scope', '$element', '$timeout', '$filter', '$$uisDebounce', 'uisRepeatParser', 'uiSelectMinErr', 'uiSelectConfig', '$parse', '$injector', '$window',
function($scope, $element, $timeout, $filter, $$uisDebounce, RepeatParser, uiSelectMinErr, uiSelectConfig, $parse, $injector, $window) {
var ctrl = this;
var EMPTY_SEARCH = '';
ctrl.placeholder = uiSelectConfig.placeholder;
ctrl.searchEnabled = uiSelectConfig.searchEnabled;
ctrl.sortable = uiSelectConfig.sortable;
ctrl.refreshDelay = uiSelectConfig.refreshDelay;
ctrl.paste = uiSelectConfig.paste;
ctrl.resetSearchInput = uiSelectConfig.resetSearchInput;
ctrl.refreshing = false;
ctrl.spinnerEnabled = uiSelectConfig.spinnerEnabled;
ctrl.spinnerClass = uiSelectConfig.spinnerClass;
ctrl.removeSelected = uiSelectConfig.removeSelected; //If selected item(s) should be removed from dropdown list
ctrl.closeOnSelect = true; //Initialized inside uiSelect directive link function
ctrl.skipFocusser = false; //Set to true to avoid returning focus to ctrl when item is selected
ctrl.search = EMPTY_SEARCH;
ctrl.activeIndex = 0; //Dropdown of choices
ctrl.items = []; //All available choices
ctrl.open = false;
ctrl.focus = false;
ctrl.disabled = false;
ctrl.selected = undefined;
ctrl.dropdownPosition = 'auto';
ctrl.focusser = undefined; //Reference to input element used to handle focus events
ctrl.multiple = undefined; // Initialized inside uiSelect directive link function
ctrl.disableChoiceExpression = undefined; // Initialized inside uiSelectChoices directive link function
ctrl.tagging = {isActivated: false, fct: undefined};
ctrl.taggingTokens = {isActivated: false, tokens: undefined};
ctrl.lockChoiceExpression = undefined; // Initialized inside uiSelectMatch directive link function
ctrl.clickTriggeredSelect = false;
ctrl.$filter = $filter;
ctrl.$element = $element;
// Use $injector to check for $animate and store a reference to it
ctrl.$animate = (function () {
try {
return $injector.get('$animate');
} catch (err) {
// $animate does not exist
return null;
}
})();
ctrl.searchInput = $element.querySelectorAll('input.ui-select-search');
if (ctrl.searchInput.length !== 1) {
throw uiSelectMinErr('searchInput', "Expected 1 input.ui-select-search but got '{0}'.", ctrl.searchInput.length);
}
ctrl.isEmpty = function() {
return isNil(ctrl.selected) || ctrl.selected === '' || (ctrl.multiple && ctrl.selected.length === 0);
};
function _findIndex(collection, predicate, thisArg){
if (collection.findIndex){
return collection.findIndex(predicate, thisArg);
} else {
var list = Object(collection);
var length = list.length >>> 0;
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
}
}
// Most of the time the user does not want to empty the search input when in typeahead mode
function _resetSearchInput() {
if (ctrl.resetSearchInput) {
ctrl.search = EMPTY_SEARCH;
//reset activeIndex
if (ctrl.selected && ctrl.items.length && !ctrl.multiple) {
ctrl.activeIndex = _findIndex(ctrl.items, function(item){
return angular.equals(this, item);
}, ctrl.selected);
}
}
}
function _groupsFilter(groups, groupNames) {
var i, j, result = [];
for(i = 0; i < groupNames.length ;i++){
for(j = 0; j < groups.length ;j++){
if(groups[j].name == [groupNames[i]]){
result.push(groups[j]);
}
}
}
return result;
}
// When the user clicks on ui-select, displays the dropdown list
ctrl.activate = function(initSearchValue, avoidReset) {
if (!ctrl.disabled && !ctrl.open) {
if(!avoidReset) _resetSearchInput();
$scope.$broadcast('uis:activate');
ctrl.open = true;
ctrl.activeIndex = ctrl.activeIndex >= ctrl.items.length ? 0 : ctrl.activeIndex;
// ensure that the index is set to zero for tagging variants
// that where first option is auto-selected
if ( ctrl.activeIndex === -1 && ctrl.taggingLabel !== false ) {
ctrl.activeIndex = 0;
}
var container = $element.querySelectorAll('.ui-select-choices-content');
var searchInput = $element.querySelectorAll('.ui-select-search');
if (ctrl.$animate && ctrl.$animate.on && ctrl.$animate.enabled(container[0])) {
var animateHandler = function(elem, phase) {
if (phase === 'start' && ctrl.items.length === 0) {
// Only focus input after the animation has finished
ctrl.$animate.off('removeClass', searchInput[0], animateHandler);
$timeout(function () {
ctrl.focusSearchInput(initSearchValue);
});
} else if (phase === 'close') {
// Only focus input after the animation has finished
ctrl.$animate.off('enter', container[0], animateHandler);
$timeout(function () {
ctrl.focusSearchInput(initSearchValue);
});
}
};
if (ctrl.items.length > 0) {
ctrl.$animate.on('enter', container[0], animateHandler);
} else {
ctrl.$animate.on('removeClass', searchInput[0], animateHandler);
}
} else {
$timeout(function () {
ctrl.focusSearchInput(initSearchValue);
if(!ctrl.tagging.isActivated && ctrl.items.length > 1) {
_ensureHighlightVisible();
}
});
}
}
else if (ctrl.open && !ctrl.searchEnabled) {
// Close the selection if we don't have search enabled, and we click on the select again
ctrl.close();
}
};
ctrl.focusSearchInput = function (initSearchValue) {
ctrl.search = initSearchValue || ctrl.search;
ctrl.searchInput[0].focus();
};
ctrl.findGroupByName = function(name) {
return ctrl.groups && ctrl.groups.filter(function(group) {
return group.name === name;
})[0];
};
ctrl.parseRepeatAttr = function(repeatAttr, groupByExp, groupFilterExp) {
function updateGroups(items) {
var groupFn = $scope.$eval(groupByExp);
ctrl.groups = [];
angular.forEach(items, function(item) {
var groupName = angular.isFunction(groupFn) ? groupFn(item) : item[groupFn];
var group = ctrl.findGroupByName(groupName);
if(group) {
group.items.push(item);
}
else {
ctrl.groups.push({name: groupName, items: [item]});
}
});
if(groupFilterExp){
var groupFilterFn = $scope.$eval(groupFilterExp);
if( angular.isFunction(groupFilterFn)){
ctrl.groups = groupFilterFn(ctrl.groups);
} else if(angular.isArray(groupFilterFn)){
ctrl.groups = _groupsFilter(ctrl.groups, groupFilterFn);
}
}
ctrl.items = [];
ctrl.groups.forEach(function(group) {
ctrl.items = ctrl.items.concat(group.items);
});
}
function setPlainItems(items) {
ctrl.items = items || [];
}
ctrl.setItemsFn = groupByExp ? updateGroups : setPlainItems;
ctrl.parserResult = RepeatParser.parse(repeatAttr);
ctrl.isGrouped = !!groupByExp;
ctrl.itemProperty = ctrl.parserResult.itemName;
//If collection is an Object, convert it to Array
var originalSource = ctrl.parserResult.source;
//When an object is used as source, we better create an array and use it as 'source'
var createArrayFromObject = function(){
var origSrc = originalSource($scope);
$scope.$uisSource = Object.keys(origSrc).map(function(v){
var result = {};
result[ctrl.parserResult.keyName] = v;
result.value = origSrc[v];
return result;
});
};
if (ctrl.parserResult.keyName){ // Check for (key,value) syntax
createArrayFromObject();
ctrl.parserResult.source = $parse('$uisSource' + ctrl.parserResult.filters);
$scope.$watch(originalSource, function(newVal, oldVal){
if (newVal !== oldVal) createArrayFromObject();
}, true);
}
ctrl.refreshItems = function (data){
data = data || ctrl.parserResult.source($scope);
var selectedItems = ctrl.selected;
//TODO should implement for single mode removeSelected
if (ctrl.isEmpty() || (angular.isArray(selectedItems) && !selectedItems.length) || !ctrl.multiple || !ctrl.removeSelected) {
ctrl.setItemsFn(data);
}else{
if ( data !== undefined && data !== null ) {
var filteredItems = data.filter(function(i) {
return angular.isArray(selectedItems) ? selectedItems.every(function(selectedItem) {
return !angular.equals(i, selectedItem);
}) : !angular.equals(i, selectedItems);
});
ctrl.setItemsFn(filteredItems);
}
}
if (ctrl.dropdownPosition === 'auto' || ctrl.dropdownPosition === 'up'){
$scope.calculateDropdownPos();
}
$scope.$broadcast('uis:refresh');
};
// See https://github.com/angular/angular.js/blob/v1.2.15/src/ng/directive/ngRepeat.js#L259
$scope.$watchCollection(ctrl.parserResult.source, function(items) {
if (items === undefined || items === null) {
// If the user specifies undefined or null => reset the collection
// Special case: items can be undefined if the user did not initialized the collection on the scope
// i.e $scope.addresses = [] is missing
ctrl.items = [];
} else {
if (!angular.isArray(items)) {
throw uiSelectMinErr('items', "Expected an array but got '{0}'.", items);
} else {
//Remove already selected items (ex: while searching)
//TODO Should add a test
ctrl.refreshItems(items);
//update the view value with fresh data from items, if there is a valid model value
if(angular.isDefined(ctrl.ngModel.$modelValue)) {
ctrl.ngModel.$modelValue = null; //Force scope model value and ngModel value to be out of sync to re-run formatters
}
}
}
});
};
var _refreshDelayPromise;
/**
* Typeahead mode: lets the user refresh the collection using his own function.
*
* See Expose $select.search for external / remote filtering https://github.com/angular-ui/ui-select/pull/31
*/
ctrl.refresh = function(refreshAttr) {
if (refreshAttr !== undefined) {
// Debounce
// See https://github.com/angular-ui/bootstrap/blob/0.10.0/src/typeahead/typeahead.js#L155
// FYI AngularStrap typeahead does not have debouncing: https://github.com/mgcrea/angular-strap/blob/v2.0.0-rc.4/src/typeahead/typeahead.js#L177
if (_refreshDelayPromise) {
$timeout.cancel(_refreshDelayPromise);
}
_refreshDelayPromise = $timeout(function() {
if ($scope.$select.search.length >= $scope.$select.minimumInputLength) {
var refreshPromise = $scope.$eval(refreshAttr);
if (refreshPromise && angular.isFunction(refreshPromise.then) && !ctrl.refreshing) {
ctrl.refreshing = true;
refreshPromise.finally(function() {
ctrl.refreshing = false;
});
}
}
}, ctrl.refreshDelay);
}
};
ctrl.isActive = function(itemScope) {
if ( !ctrl.open ) {
return false;
}
var itemIndex = ctrl.items.indexOf(itemScope[ctrl.itemProperty]);
var isActive = itemIndex == ctrl.activeIndex;
if ( !isActive || itemIndex < 0 ) {
return false;
}
if (isActive && !angular.isUndefined(ctrl.onHighlightCallback)) {
itemScope.$eval(ctrl.onHighlightCallback);
}
return isActive;
};
var _isItemSelected = function (item) {
return (ctrl.selected && angular.isArray(ctrl.selected) &&
ctrl.selected.filter(function (selection) { return angular.equals(selection, item); }).length > 0);
};
var disabledItems = [];
function _updateItemDisabled(item, isDisabled) {
var disabledItemIndex = disabledItems.indexOf(item);
if (isDisabled && disabledItemIndex === -1) {
disabledItems.push(item);
}
if (!isDisabled && disabledItemIndex > -1) {
disabledItems.splice(disabledItemIndex, 1);
}
}
function _isItemDisabled(item) {
return disabledItems.indexOf(item) > -1;
}
ctrl.isDisabled = function(itemScope) {
if (!ctrl.open) return;
var item = itemScope[ctrl.itemProperty];
var itemIndex = ctrl.items.indexOf(item);
var isDisabled = false;
if (itemIndex >= 0 && (angular.isDefined(ctrl.disableChoiceExpression) || ctrl.multiple)) {
if (item.isTag) return false;
if (ctrl.multiple) {
isDisabled = _isItemSelected(item);
}
if (!isDisabled && angular.isDefined(ctrl.disableChoiceExpression)) {
isDisabled = !!(itemScope.$eval(ctrl.disableChoiceExpression));
}
_updateItemDisabled(item, isDisabled);
}
return isDisabled;
};
// When the user selects an item with ENTER or clicks the dropdown
ctrl.select = function(item, skipFocusser, $event) {
if (isNil(item) || !_isItemDisabled(item)) {
if ( ! ctrl.items && ! ctrl.search && ! ctrl.tagging.isActivated) return;
if (!item || !_isItemDisabled(item)) {
// if click is made on existing item, prevent from tagging, ctrl.search does not matter
ctrl.clickTriggeredSelect = false;
if($event && ($event.type === 'click' || $event.type === 'touchend') && item)
ctrl.clickTriggeredSelect = true;
if(ctrl.tagging.isActivated && ctrl.clickTriggeredSelect === false) {
// if taggingLabel is disabled and item is undefined we pull from ctrl.search
if ( ctrl.taggingLabel === false ) {
if ( ctrl.activeIndex < 0 ) {
if (item === undefined) {
item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : ctrl.search;
}
if (!item || angular.equals( ctrl.items[0], item ) ) {
return;
}
} else {
// keyboard nav happened first, user selected from dropdown
item = ctrl.items[ctrl.activeIndex];
}
} else {
// tagging always operates at index zero, taggingLabel === false pushes
// the ctrl.search value without having it injected
if ( ctrl.activeIndex === 0 ) {
// ctrl.tagging pushes items to ctrl.items, so we only have empty val
// for `item` if it is a detected duplicate
if ( item === undefined ) return;
// create new item on the fly if we don't already have one;
// use tagging function if we have one
if ( ctrl.tagging.fct !== undefined && typeof item === 'string' ) {
item = ctrl.tagging.fct(item);
if (!item) return;
// if item type is 'string', apply the tagging label
} else if ( typeof item === 'string' ) {
// trim the trailing space
item = item.replace(ctrl.taggingLabel,'').trim();
}
}
}
// search ctrl.selected for dupes potentially caused by tagging and return early if found
if (_isItemSelected(item)) {
ctrl.close(skipFocusser);
return;
}
}
_resetSearchInput();
$scope.$broadcast('uis:select', item);
if (ctrl.closeOnSelect) {
ctrl.close(skipFocusser);
}
}
}
};
// Closes the dropdown
ctrl.close = function(skipFocusser) {
if (!ctrl.open) return;
if (ctrl.ngModel && ctrl.ngModel.$setTouched) ctrl.ngModel.$setTouched();
ctrl.open = false;
_resetSearchInput();
$scope.$broadcast('uis:close', skipFocusser);
};
ctrl.setFocus = function(){
if (!ctrl.focus) ctrl.focusInput[0].focus();
};
ctrl.clear = function($event) {
ctrl.select(null);
$event.stopPropagation();
$timeout(function() {
ctrl.focusser[0].focus();
}, 0, false);
};
// Toggle dropdown
ctrl.toggle = function(e) {
if (ctrl.open) {
ctrl.close();
e.preventDefault();
e.stopPropagation();
} else {
ctrl.activate();
}
};
// Set default function for locked choices - avoids unnecessary
// logic if functionality is not being used
ctrl.isLocked = function () {
return false;
};
$scope.$watch(function () {
return angular.isDefined(ctrl.lockChoiceExpression) && ctrl.lockChoiceExpression !== "";
}, _initaliseLockedChoices);
function _initaliseLockedChoices(doInitalise) {
if(!doInitalise) return;
var lockedItems = [];
function _updateItemLocked(item, isLocked) {
var lockedItemIndex = lockedItems.indexOf(item);
if (isLocked && lockedItemIndex === -1) {
lockedItems.push(item);
}
if (!isLocked && lockedItemIndex > -1) {
lockedItems.splice(lockedItemIndex, 1);
}
}
function _isItemlocked(item) {
return lockedItems.indexOf(item) > -1;
}
ctrl.isLocked = function (itemScope, itemIndex) {
var isLocked = false,
item = ctrl.selected[itemIndex];
if(item) {
if (itemScope) {
isLocked = !!(itemScope.$eval(ctrl.lockChoiceExpression));
_updateItemLocked(item, isLocked);
} else {
isLocked = _isItemlocked(item);
}
}
return isLocked;
};
}
var sizeWatch = null;
var updaterScheduled = false;
ctrl.sizeSearchInput = function() {
var input = ctrl.searchInput[0],
container = ctrl.$element[0],
calculateContainerWidth = function() {
// Return the container width only if the search input is visible
return container.clientWidth * !!input.offsetParent;
},
updateIfVisible = function(containerWidth) {
if (containerWidth === 0) {
return false;
}
var inputWidth = containerWidth - input.offsetLeft;
if (inputWidth < 50) inputWidth = containerWidth;
ctrl.searchInput.css('width', inputWidth+'px');
return true;
};
ctrl.searchInput.css('width', '10px');
$timeout(function() { //Give tags time to render correctly
if (sizeWatch === null && !updateIfVisible(calculateContainerWidth())) {
sizeWatch = $scope.$watch(function() {
if (!updaterScheduled) {
updaterScheduled = true;
$scope.$$postDigest(function() {
updaterScheduled = false;
if (updateIfVisible(calculateContainerWidth())) {
sizeWatch();
sizeWatch = null;
}
});
}
}, angular.noop);
}
});
};
function _handleDropDownSelection(key) {
var processed = true;
switch (key) {
case KEY.DOWN:
if (!ctrl.open && ctrl.multiple) ctrl.activate(false, true); //In case its the search input in 'multiple' mode
else if (ctrl.activeIndex < ctrl.items.length - 1) {
var idx = ++ctrl.activeIndex;
while(_isItemDisabled(ctrl.items[idx]) && idx < ctrl.items.length) {
ctrl.activeIndex = ++idx;
}
}
break;
case KEY.UP:
var minActiveIndex = (ctrl.search.length === 0 && ctrl.tagging.isActivated) ? -1 : 0;
if (!ctrl.open && ctrl.multiple) ctrl.activate(false, true); //In case its the search input in 'multiple' mode
else if (ctrl.activeIndex > minActiveIndex) {
var idxmin = --ctrl.activeIndex;
while(_isItemDisabled(ctrl.items[idxmin]) && idxmin > minActiveIndex) {
ctrl.activeIndex = --idxmin;
}
}
break;
case KEY.TAB:
if (!ctrl.multiple || ctrl.open) ctrl.select(ctrl.items[ctrl.activeIndex], true);
break;
case KEY.ENTER:
if(ctrl.open && (ctrl.tagging.isActivated || ctrl.activeIndex >= 0)){
ctrl.select(ctrl.items[ctrl.activeIndex], ctrl.skipFocusser); // Make sure at least one dropdown item is highlighted before adding if not in tagging mode
} else {
ctrl.activate(false, true); //In case its the search input in 'multiple' mode
}
break;
case KEY.ESC:
ctrl.close();
break;
default:
processed = false;
}
return processed;
}
// Bind to keyboard shortcuts
ctrl.searchInput.on('keydown', function(e) {
var key = e.which;
if (~[KEY.ENTER,KEY.ESC].indexOf(key)){
e.preventDefault();
e.stopPropagation();
}
$scope.$apply(function() {
var tagged = false;
if (ctrl.items.length > 0 || ctrl.tagging.isActivated) {
if(!_handleDropDownSelection(key) && !ctrl.searchEnabled) {
e.preventDefault();
e.stopPropagation();
}
if ( ctrl.taggingTokens.isActivated ) {
for (var i = 0; i < ctrl.taggingTokens.tokens.length; i++) {
if ( ctrl.taggingTokens.tokens[i] === KEY.MAP[e.keyCode] ) {
// make sure there is a new value to push via tagging
if ( ctrl.search.length > 0 ) {
tagged = true;
}
}
}
if ( tagged ) {
$timeout(function() {
ctrl.searchInput.triggerHandler('tagged');
var newItem = ctrl.search.replace(KEY.MAP[e.keyCode],'').trim();
if ( ctrl.tagging.fct ) {
newItem = ctrl.tagging.fct( newItem );
}
if (newItem) ctrl.select(newItem, true);
});
}
}
}
});
if(KEY.isVerticalMovement(key) && ctrl.items.length > 0){
_ensureHighlightVisible();
}
if (key === KEY.ENTER || key === KEY.ESC) {
e.preventDefault();
e.stopPropagation();
}
});
ctrl.searchInput.on('paste', function (e) {
var data;
if (window.clipboardData && window.clipboardData.getData) { // IE
data = window.clipboardData.getData('Text');
} else {
data = (e.originalEvent || e).clipboardData.getData('text/plain');
}
// Prepend the current input field text to the paste buffer.
data = ctrl.search + data;
if (data && data.length > 0) {
// If tagging try to split by tokens and add items
if (ctrl.taggingTokens.isActivated) {
var items = [];
for (var i = 0; i < ctrl.taggingTokens.tokens.length; i++) { // split by first token that is contained in data
var separator = KEY.toSeparator(ctrl.taggingTokens.tokens[i]) || ctrl.taggingTokens.tokens[i];
if (data.indexOf(separator) > -1) {
items = data.split(separator);
break; // only split by one token
}
}
if (items.length === 0) {
items = [data];
}
var oldsearch = ctrl.search;
angular.forEach(items, function (item) {
var newItem = ctrl.tagging.fct ? ctrl.tagging.fct(item) : item;
if (newItem) {
ctrl.select(newItem, true);
}
});
ctrl.search = oldsearch || EMPTY_SEARCH;
e.preventDefault();
e.stopPropagation();
} else if (ctrl.paste) {
ctrl.paste(data);
ctrl.search = EMPTY_SEARCH;
e.preventDefault();
e.stopPropagation();
}
}
});
ctrl.searchInput.on('tagged', function() {
$timeout(function() {
_resetSearchInput();
});
});
// See https://github.com/ivaynberg/select2/blob/3.4.6/select2.js#L1431
function _ensureHighlightVisible() {
var container = $element.querySelectorAll('.ui-select-choices-content');
var choices = container.querySelectorAll('.ui-select-choices-row');
if (choices.length < 1) {
throw uiSelectMinErr('choices', "Expected multiple .ui-select-choices-row but got '{0}'.", choices.length);
}
if (ctrl.activeIndex < 0) {
return;
}
var highlighted = choices[ctrl.activeIndex];
var posY = highlighted.offsetTop + highlighted.clientHeight - container[0].scrollTop;
var height = container[0].offsetHeight;
if (posY > height) {
container[0].scrollTop += posY - height;
} else if (posY < highlighted.clientHeight) {
if (ctrl.isGrouped && ctrl.activeIndex === 0)
container[0].scrollTop = 0; //To make group header visible when going all the way up
else
container[0].scrollTop -= highlighted.clientHeight - posY;
}
}
var onResize = $$uisDebounce(function() {
ctrl.sizeSearchInput();
}, 50);
angular.element($window).bind('resize', onResize);
$scope.$on('$destroy', function() {
ctrl.searchInput.off('keyup keydown tagged blur paste');
angular.element($window).off('resize', onResize);
});
$scope.$watch('$select.activeIndex', function(activeIndex) {
if (activeIndex)
$element.find('input').attr(
'aria-activedescendant',
'ui-select-choices-row-' + ctrl.generatedId + '-' + activeIndex);
});
$scope.$watch('$select.open', function(open) {
if (!open)
$element.find('input').removeAttr('aria-activedescendant');
});
}]);

423
js/vendor/angular-ui-select/src/uiSelectDirective.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,423 @@
uis.directive('uiSelect',
['$document', 'uiSelectConfig', 'uiSelectMinErr', 'uisOffset', '$compile', '$parse', '$timeout',
function($document, uiSelectConfig, uiSelectMinErr, uisOffset, $compile, $parse, $timeout) {
return {
restrict: 'EA',
templateUrl: function(tElement, tAttrs) {
var theme = tAttrs.theme || uiSelectConfig.theme;
return theme + (angular.isDefined(tAttrs.multiple) ? '/select-multiple.tpl.html' : '/select.tpl.html');
},
replace: true,
transclude: true,
require: ['uiSelect', '^ngModel'],
scope: true,
controller: 'uiSelectCtrl',
controllerAs: '$select',
compile: function(tElement, tAttrs) {
// Allow setting ngClass on uiSelect
var match = /{(.*)}\s*{(.*)}/.exec(tAttrs.ngClass);
if(match) {
var combined = '{'+ match[1] +', '+ match[2] +'}';
tAttrs.ngClass = combined;
tElement.attr('ng-class', combined);
}
//Multiple or Single depending if multiple attribute presence
if (angular.isDefined(tAttrs.multiple))
tElement.append('<ui-select-multiple/>').removeAttr('multiple');
else
tElement.append('<ui-select-single/>');
if (tAttrs.inputId)
tElement.querySelectorAll('input.ui-select-search')[0].id = tAttrs.inputId;
return function(scope, element, attrs, ctrls, transcludeFn) {
var $select = ctrls[0];
var ngModel = ctrls[1];
$select.generatedId = uiSelectConfig.generateId();
$select.baseTitle = attrs.title || 'Select box';
$select.focusserTitle = $select.baseTitle + ' focus';
$select.focusserId = 'focusser-' + $select.generatedId;
$select.closeOnSelect = function() {
if (angular.isDefined(attrs.closeOnSelect)) {
return $parse(attrs.closeOnSelect)();
} else {
return uiSelectConfig.closeOnSelect;
}
}();
scope.$watch('skipFocusser', function() {
var skipFocusser = scope.$eval(attrs.skipFocusser);
$select.skipFocusser = skipFocusser !== undefined ? skipFocusser : uiSelectConfig.skipFocusser;
});
$select.onSelectCallback = $parse(attrs.onSelect);
$select.onRemoveCallback = $parse(attrs.onRemove);
//Set reference to ngModel from uiSelectCtrl
$select.ngModel = ngModel;
$select.choiceGrouped = function(group){
return $select.isGrouped && group && group.name;
};
if(attrs.tabindex){
attrs.$observe('tabindex', function(value) {
$select.focusInput.attr('tabindex', value);
element.removeAttr('tabindex');
});
}
scope.$watch(function () { return scope.$eval(attrs.searchEnabled); }, function(newVal) {
$select.searchEnabled = newVal !== undefined ? newVal : uiSelectConfig.searchEnabled;
});
scope.$watch('sortable', function() {
var sortable = scope.$eval(attrs.sortable);
$select.sortable = sortable !== undefined ? sortable : uiSelectConfig.sortable;
});
attrs.$observe('backspaceReset', function() {
// $eval() is needed otherwise we get a string instead of a boolean
var backspaceReset = scope.$eval(attrs.backspaceReset);
$select.backspaceReset = backspaceReset !== undefined ? backspaceReset : true;
});
attrs.$observe('limit', function() {
//Limit the number of selections allowed
$select.limit = (angular.isDefined(attrs.limit)) ? parseInt(attrs.limit, 10) : undefined;
});
scope.$watch('removeSelected', function() {
var removeSelected = scope.$eval(attrs.removeSelected);
$select.removeSelected = removeSelected !== undefined ? removeSelected : uiSelectConfig.removeSelected;
});
attrs.$observe('disabled', function() {
// No need to use $eval() (thanks to ng-disabled) since we already get a boolean instead of a string
$select.disabled = attrs.disabled !== undefined ? attrs.disabled : false;
});
attrs.$observe('resetSearchInput', function() {
// $eval() is needed otherwise we get a string instead of a boolean
var resetSearchInput = scope.$eval(attrs.resetSearchInput);
$select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true;
});
attrs.$observe('paste', function() {
$select.paste = scope.$eval(attrs.paste);
});
attrs.$observe('tagging', function() {
if(attrs.tagging !== undefined)
{
// $eval() is needed otherwise we get a string instead of a boolean
var taggingEval = scope.$eval(attrs.tagging);
$select.tagging = {isActivated: true, fct: taggingEval !== true ? taggingEval : undefined};
}
else
{
$select.tagging = {isActivated: false, fct: undefined};
}
});
attrs.$observe('taggingLabel', function() {
if(attrs.tagging !== undefined )
{
// check eval for FALSE, in this case, we disable the labels
// associated with tagging
if ( attrs.taggingLabel === 'false' ) {
$select.taggingLabel = false;
}
else
{
$select.taggingLabel = attrs.taggingLabel !== undefined ? attrs.taggingLabel : '(new)';
}
}
});
attrs.$observe('taggingTokens', function() {
if (attrs.tagging !== undefined) {
var tokens = attrs.taggingTokens !== undefined ? attrs.taggingTokens.split('|') : [',','ENTER'];
$select.taggingTokens = {isActivated: true, tokens: tokens };
}
});
attrs.$observe('spinnerEnabled', function() {
// $eval() is needed otherwise we get a string instead of a boolean
var spinnerEnabled = scope.$eval(attrs.spinnerEnabled);
$select.spinnerEnabled = spinnerEnabled !== undefined ? spinnerEnabled : uiSelectConfig.spinnerEnabled;
});
attrs.$observe('spinnerClass', function() {
var spinnerClass = attrs.spinnerClass;
$select.spinnerClass = spinnerClass !== undefined ? attrs.spinnerClass : uiSelectConfig.spinnerClass;
});
//Automatically gets focus when loaded
if (angular.isDefined(attrs.autofocus)){
$timeout(function(){
$select.setFocus();
});
}
//Gets focus based on scope event name (e.g. focus-on='SomeEventName')
if (angular.isDefined(attrs.focusOn)){
scope.$on(attrs.focusOn, function() {
$timeout(function(){
$select.setFocus();
});
});
}
function onDocumentClick(e) {
if (!$select.open) return; //Skip it if dropdown is close
var contains = false;
if (window.jQuery) {
// Firefox 3.6 does not support element.contains()
// See Node.contains https://developer.mozilla.org/en-US/docs/Web/API/Node.contains
contains = window.jQuery.contains(element[0], e.target);
} else {
contains = element[0].contains(e.target);
}
if (!contains && !$select.clickTriggeredSelect) {
var skipFocusser;
if (!$select.skipFocusser) {
//Will lose focus only with certain targets
var focusableControls = ['input','button','textarea','select'];
var targetController = angular.element(e.target).controller('uiSelect'); //To check if target is other ui-select
skipFocusser = targetController && targetController !== $select; //To check if target is other ui-select
if (!skipFocusser) skipFocusser = ~focusableControls.indexOf(e.target.tagName.toLowerCase()); //Check if target is input, button or textarea
} else {
skipFocusser = true;
}
$select.close(skipFocusser);
scope.$digest();
}
$select.clickTriggeredSelect = false;
}
// See Click everywhere but here event http://stackoverflow.com/questions/12931369
$document.on('click', onDocumentClick);
scope.$on('$destroy', function() {
$document.off('click', onDocumentClick);
});
// Move transcluded elements to their correct position in main template
transcludeFn(scope, function(clone) {
// See Transclude in AngularJS http://blog.omkarpatil.com/2012/11/transclude-in-angularjs.html
// One day jqLite will be replaced by jQuery and we will be able to write:
// var transcludedElement = clone.filter('.my-class')
// instead of creating a hackish DOM element:
var transcluded = angular.element('<div>').append(clone);
var transcludedMatch = transcluded.querySelectorAll('.ui-select-match');
transcludedMatch.removeAttr('ui-select-match'); //To avoid loop in case directive as attr
transcludedMatch.removeAttr('data-ui-select-match'); // Properly handle HTML5 data-attributes
if (transcludedMatch.length !== 1) {
throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-match but got '{0}'.", transcludedMatch.length);
}
element.querySelectorAll('.ui-select-match').replaceWith(transcludedMatch);
var transcludedChoices = transcluded.querySelectorAll('.ui-select-choices');
transcludedChoices.removeAttr('ui-select-choices'); //To avoid loop in case directive as attr
transcludedChoices.removeAttr('data-ui-select-choices'); // Properly handle HTML5 data-attributes
if (transcludedChoices.length !== 1) {
throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-choices but got '{0}'.", transcludedChoices.length);
}
element.querySelectorAll('.ui-select-choices').replaceWith(transcludedChoices);
var transcludedNoChoice = transcluded.querySelectorAll('.ui-select-no-choice');
transcludedNoChoice.removeAttr('ui-select-no-choice'); //To avoid loop in case directive as attr
transcludedNoChoice.removeAttr('data-ui-select-no-choice'); // Properly handle HTML5 data-attributes
if (transcludedNoChoice.length == 1) {
element.querySelectorAll('.ui-select-no-choice').replaceWith(transcludedNoChoice);
}
});
// Support for appending the select field to the body when its open
var appendToBody = scope.$eval(attrs.appendToBody);
if (appendToBody !== undefined ? appendToBody : uiSelectConfig.appendToBody) {
scope.$watch('$select.open', function(isOpen) {
if (isOpen) {
positionDropdown();
} else {
resetDropdown();
}
});
// Move the dropdown back to its original location when the scope is destroyed. Otherwise
// it might stick around when the user routes away or the select field is otherwise removed
scope.$on('$destroy', function() {
resetDropdown();
});
}
// Hold on to a reference to the .ui-select-container element for appendToBody support
var placeholder = null,
originalWidth = '';
function positionDropdown() {
// Remember the absolute position of the element
var offset = uisOffset(element);
// Clone the element into a placeholder element to take its original place in the DOM
placeholder = angular.element('<div class="ui-select-placeholder"></div>');
placeholder[0].style.width = offset.width + 'px';
placeholder[0].style.height = offset.height + 'px';
element.after(placeholder);
// Remember the original value of the element width inline style, so it can be restored
// when the dropdown is closed
originalWidth = element[0].style.width;
// Now move the actual dropdown element to the end of the body
$document.find('body').append(element);
element[0].style.position = 'absolute';
element[0].style.left = offset.left + 'px';
element[0].style.top = offset.top + 'px';
element[0].style.width = offset.width + 'px';
}
function resetDropdown() {
if (placeholder === null) {
// The dropdown has not actually been display yet, so there's nothing to reset
return;
}
// Move the dropdown element back to its original location in the DOM
placeholder.replaceWith(element);
placeholder = null;
element[0].style.position = '';
element[0].style.left = '';
element[0].style.top = '';
element[0].style.width = originalWidth;
// Set focus back on to the moved element
$select.setFocus();
}
// Hold on to a reference to the .ui-select-dropdown element for direction support.
var dropdown = null,
directionUpClassName = 'direction-up';
// Support changing the direction of the dropdown if there isn't enough space to render it.
scope.$watch('$select.open', function() {
if ($select.dropdownPosition === 'auto' || $select.dropdownPosition === 'up'){
scope.calculateDropdownPos();
}
});
var setDropdownPosUp = function(offset, offsetDropdown){
offset = offset || uisOffset(element);
offsetDropdown = offsetDropdown || uisOffset(dropdown);
dropdown[0].style.position = 'absolute';
dropdown[0].style.top = (offsetDropdown.height * -1) + 'px';
element.addClass(directionUpClassName);
};
var setDropdownPosDown = function(offset, offsetDropdown){
element.removeClass(directionUpClassName);
offset = offset || uisOffset(element);
offsetDropdown = offsetDropdown || uisOffset(dropdown);
dropdown[0].style.position = '';
dropdown[0].style.top = '';
};
var calculateDropdownPosAfterAnimation = function() {
// Delay positioning the dropdown until all choices have been added so its height is correct.
$timeout(function() {
if ($select.dropdownPosition === 'up') {
//Go UP
setDropdownPosUp();
} else {
//AUTO
element.removeClass(directionUpClassName);
var offset = uisOffset(element);
var offsetDropdown = uisOffset(dropdown);
//https://code.google.com/p/chromium/issues/detail?id=342307#c4
var scrollTop = $document[0].documentElement.scrollTop || $document[0].body.scrollTop; //To make it cross browser (blink, webkit, IE, Firefox).
// Determine if the direction of the dropdown needs to be changed.
if (offset.top + offset.height + offsetDropdown.height > scrollTop + $document[0].documentElement.clientHeight) {
//Go UP
setDropdownPosUp(offset, offsetDropdown);
}else{
//Go DOWN
setDropdownPosDown(offset, offsetDropdown);
}
}
// Display the dropdown once it has been positioned.
dropdown[0].style.opacity = 1;
});
};
var opened = false;
scope.calculateDropdownPos = function() {
if ($select.open) {
dropdown = angular.element(element).querySelectorAll('.ui-select-dropdown');
if (dropdown.length === 0) {
return;
}
// Hide the dropdown so there is no flicker until $timeout is done executing.
if ($select.search === '' && !opened) {
dropdown[0].style.opacity = 0;
opened = true;
}
if (!uisOffset(dropdown).height && $select.$animate && $select.$animate.on && $select.$animate.enabled(dropdown)) {
var needsCalculated = true;
$select.$animate.on('enter', dropdown, function (elem, phase) {
if (phase === 'close' && needsCalculated) {
calculateDropdownPosAfterAnimation();
needsCalculated = false;
}
});
} else {
calculateDropdownPosAfterAnimation();
}
} else {
if (dropdown === null || dropdown.length === 0) {
return;
}
// Reset the position of the dropdown.
dropdown[0].style.opacity = 0;
dropdown[0].style.position = '';
dropdown[0].style.top = '';
element.removeClass(directionUpClassName);
}
};
};
}
};
}]);

48
js/vendor/angular-ui-select/src/uiSelectMatchDirective.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,48 @@
uis.directive('uiSelectMatch', ['uiSelectConfig', function(uiSelectConfig) {
return {
restrict: 'EA',
require: '^uiSelect',
replace: true,
transclude: true,
templateUrl: function(tElement) {
// Needed so the uiSelect can detect the transcluded content
tElement.addClass('ui-select-match');
var parent = tElement.parent();
// Gets theme attribute from parent (ui-select)
var theme = getAttribute(parent, 'theme') || uiSelectConfig.theme;
var multi = angular.isDefined(getAttribute(parent, 'multiple'));
return theme + (multi ? '/match-multiple.tpl.html' : '/match.tpl.html');
},
link: function(scope, element, attrs, $select) {
$select.lockChoiceExpression = attrs.uiLockChoice;
attrs.$observe('placeholder', function(placeholder) {
$select.placeholder = placeholder !== undefined ? placeholder : uiSelectConfig.placeholder;
});
function setAllowClear(allow) {
$select.allowClear = (angular.isDefined(allow)) ? (allow === '') ? true : (allow.toLowerCase() === 'true') : false;
}
attrs.$observe('allowClear', setAllowClear);
setAllowClear(attrs.allowClear);
if($select.multiple){
$select.sizeSearchInput();
}
}
};
function getAttribute(elem, attribute) {
if (elem[0].hasAttribute(attribute))
return elem.attr(attribute);
if (elem[0].hasAttribute('data-' + attribute))
return elem.attr('data-' + attribute);
if (elem[0].hasAttribute('x-' + attribute))
return elem.attr('x-' + attribute);
}
}]);

460
js/vendor/angular-ui-select/src/uiSelectMultipleDirective.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,460 @@
uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelectMinErr, $timeout) {
return {
restrict: 'EA',
require: ['^uiSelect', '^ngModel'],
controller: ['$scope','$timeout', function($scope, $timeout){
var ctrl = this,
$select = $scope.$select,
ngModel;
if (angular.isUndefined($select.selected))
$select.selected = [];
//Wait for link fn to inject it
$scope.$evalAsync(function(){ ngModel = $scope.ngModel; });
ctrl.activeMatchIndex = -1;
ctrl.updateModel = function(){
ngModel.$setViewValue(Date.now()); //Set timestamp as a unique string to force changes
ctrl.refreshComponent();
};
ctrl.refreshComponent = function(){
//Remove already selected items
//e.g. When user clicks on a selection, the selected array changes and
//the dropdown should remove that item
if($select.refreshItems){
$select.refreshItems();
}
if($select.sizeSearchInput){
$select.sizeSearchInput();
}
};
// Remove item from multiple select
ctrl.removeChoice = function(index){
// if the choice is locked, don't remove it
if($select.isLocked(null, index)) return false;
var removedChoice = $select.selected[index];
var locals = {};
locals[$select.parserResult.itemName] = removedChoice;
$select.selected.splice(index, 1);
ctrl.activeMatchIndex = -1;
$select.sizeSearchInput();
// Give some time for scope propagation.
$timeout(function(){
$select.onRemoveCallback($scope, {
$item: removedChoice,
$model: $select.parserResult.modelMapper($scope, locals)
});
});
ctrl.updateModel();
return true;
};
ctrl.getPlaceholder = function(){
//Refactor single?
if($select.selected && $select.selected.length) return;
return $select.placeholder;
};
}],
controllerAs: '$selectMultiple',
link: function(scope, element, attrs, ctrls) {
var $select = ctrls[0];
var ngModel = scope.ngModel = ctrls[1];
var $selectMultiple = scope.$selectMultiple;
//$select.selected = raw selected objects (ignoring any property binding)
$select.multiple = true;
//Input that will handle focus
$select.focusInput = $select.searchInput;
//Properly check for empty if set to multiple
ngModel.$isEmpty = function(value) {
return !value || value.length === 0;
};
//From view --> model
ngModel.$parsers.unshift(function () {
var locals = {},
result,
resultMultiple = [];
for (var j = $select.selected.length - 1; j >= 0; j--) {
locals = {};
locals[$select.parserResult.itemName] = $select.selected[j];
result = $select.parserResult.modelMapper(scope, locals);
resultMultiple.unshift(result);
}
return resultMultiple;
});
// From model --> view
ngModel.$formatters.unshift(function (inputValue) {
var data = $select.parserResult && $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search
locals = {},
result;
if (!data) return inputValue;
var resultMultiple = [];
var checkFnMultiple = function(list, value){
if (!list || !list.length) return;
for (var p = list.length - 1; p >= 0; p--) {
locals[$select.parserResult.itemName] = list[p];
result = $select.parserResult.modelMapper(scope, locals);
if($select.parserResult.trackByExp){
var propsItemNameMatches = /(\w*)\./.exec($select.parserResult.trackByExp);
var matches = /\.([^\s]+)/.exec($select.parserResult.trackByExp);
if(propsItemNameMatches && propsItemNameMatches.length > 0 && propsItemNameMatches[1] == $select.parserResult.itemName){
if(matches && matches.length>0 && result[matches[1]] == value[matches[1]]){
resultMultiple.unshift(list[p]);
return true;
}
}
}
if (angular.equals(result,value)){
resultMultiple.unshift(list[p]);
return true;
}
}
return false;
};
if (!inputValue) return resultMultiple; //If ngModel was undefined
for (var k = inputValue.length - 1; k >= 0; k--) {
//Check model array of currently selected items
if (!checkFnMultiple($select.selected, inputValue[k])){
//Check model array of all items available
if (!checkFnMultiple(data, inputValue[k])){
//If not found on previous lists, just add it directly to resultMultiple
resultMultiple.unshift(inputValue[k]);
}
}
}
return resultMultiple;
});
//Watch for external model changes
scope.$watchCollection(function(){ return ngModel.$modelValue; }, function(newValue, oldValue) {
if (oldValue != newValue){
//update the view value with fresh data from items, if there is a valid model value
if(angular.isDefined(ngModel.$modelValue)) {
ngModel.$modelValue = null; //Force scope model value and ngModel value to be out of sync to re-run formatters
}
$selectMultiple.refreshComponent();
}
});
ngModel.$render = function() {
// Make sure that model value is array
if(!angular.isArray(ngModel.$viewValue)){
// Have tolerance for null or undefined values
if (isNil(ngModel.$viewValue)){
ngModel.$viewValue = [];
} else {
throw uiSelectMinErr('multiarr', "Expected model value to be array but got '{0}'", ngModel.$viewValue);
}
}
$select.selected = ngModel.$viewValue;
$selectMultiple.refreshComponent();
scope.$evalAsync(); //To force $digest
};
scope.$on('uis:select', function (event, item) {
if($select.selected.length >= $select.limit) {
return;
}
$select.selected.push(item);
var locals = {};
locals[$select.parserResult.itemName] = item;
$timeout(function(){
$select.onSelectCallback(scope, {
$item: item,
$model: $select.parserResult.modelMapper(scope, locals)
});
});
$selectMultiple.updateModel();
});
scope.$on('uis:activate', function () {
$selectMultiple.activeMatchIndex = -1;
});
scope.$watch('$select.disabled', function(newValue, oldValue) {
// As the search input field may now become visible, it may be necessary to recompute its size
if (oldValue && !newValue) $select.sizeSearchInput();
});
$select.searchInput.on('keydown', function(e) {
var key = e.which;
scope.$apply(function() {
var processed = false;
// var tagged = false; //Checkme
if(KEY.isHorizontalMovement(key)){
processed = _handleMatchSelection(key);
}
if (processed && key != KEY.TAB) {
//TODO Check si el tab selecciona aun correctamente
//Crear test
e.preventDefault();
e.stopPropagation();
}
});
});
function _getCaretPosition(el) {
if(angular.isNumber(el.selectionStart)) return el.selectionStart;
// selectionStart is not supported in IE8 and we don't want hacky workarounds so we compromise
else return el.value.length;
}
// Handles selected options in "multiple" mode
function _handleMatchSelection(key){
var caretPosition = _getCaretPosition($select.searchInput[0]),
length = $select.selected.length,
// none = -1,
first = 0,
last = length-1,
curr = $selectMultiple.activeMatchIndex,
next = $selectMultiple.activeMatchIndex+1,
prev = $selectMultiple.activeMatchIndex-1,
newIndex = curr;
if(caretPosition > 0 || ($select.search.length && key == KEY.RIGHT)) return false;
$select.close();
function getNewActiveMatchIndex(){
switch(key){
case KEY.LEFT:
// Select previous/first item
if(~$selectMultiple.activeMatchIndex) return prev;
// Select last item
else return last;
break;
case KEY.RIGHT:
// Open drop-down
if(!~$selectMultiple.activeMatchIndex || curr === last){
$select.activate();
return false;
}
// Select next/last item
else return next;
break;
case KEY.BACKSPACE:
// Remove selected item and select previous/first
if(~$selectMultiple.activeMatchIndex){
if($selectMultiple.removeChoice(curr)) {
return prev;
} else {
return curr;
}
} else {
// If nothing yet selected, select last item
return last;
}
break;
case KEY.DELETE:
// Remove selected item and select next item
if(~$selectMultiple.activeMatchIndex){
$selectMultiple.removeChoice($selectMultiple.activeMatchIndex);
return curr;
}
else return false;
}
}
newIndex = getNewActiveMatchIndex();
if(!$select.selected.length || newIndex === false) $selectMultiple.activeMatchIndex = -1;
else $selectMultiple.activeMatchIndex = Math.min(last,Math.max(first,newIndex));
return true;
}
$select.searchInput.on('keyup', function(e) {
if ( ! KEY.isVerticalMovement(e.which) ) {
scope.$evalAsync( function () {
$select.activeIndex = $select.taggingLabel === false ? -1 : 0;
});
}
// Push a "create new" item into array if there is a search string
if ( $select.tagging.isActivated && $select.search.length > 0 ) {
// return early with these keys
if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC || KEY.isVerticalMovement(e.which) ) {
return;
}
// always reset the activeIndex to the first item when tagging
$select.activeIndex = $select.taggingLabel === false ? -1 : 0;
// taggingLabel === false bypasses all of this
if ($select.taggingLabel === false) return;
var items = angular.copy( $select.items );
var stashArr = angular.copy( $select.items );
var newItem;
var item;
var hasTag = false;
var dupeIndex = -1;
var tagItems;
var tagItem;
// case for object tagging via transform `$select.tagging.fct` function
if ( $select.tagging.fct !== undefined) {
tagItems = $select.$filter('filter')(items,{'isTag': true});
if ( tagItems.length > 0 ) {
tagItem = tagItems[0];
}
// remove the first element, if it has the `isTag` prop we generate a new one with each keyup, shaving the previous
if ( items.length > 0 && tagItem ) {
hasTag = true;
items = items.slice(1,items.length);
stashArr = stashArr.slice(1,stashArr.length);
}
newItem = $select.tagging.fct($select.search);
// verify the new tag doesn't match the value of a possible selection choice or an already selected item.
if (
stashArr.some(function (origItem) {
return angular.equals(origItem, newItem);
}) ||
$select.selected.some(function (origItem) {
return angular.equals(origItem, newItem);
})
) {
scope.$evalAsync(function () {
$select.activeIndex = 0;
$select.items = items;
});
return;
}
if (newItem) newItem.isTag = true;
// handle newItem string and stripping dupes in tagging string context
} else {
// find any tagging items already in the $select.items array and store them
tagItems = $select.$filter('filter')(items,function (item) {
return item.match($select.taggingLabel);
});
if ( tagItems.length > 0 ) {
tagItem = tagItems[0];
}
item = items[0];
// remove existing tag item if found (should only ever be one tag item)
if ( item !== undefined && items.length > 0 && tagItem ) {
hasTag = true;
items = items.slice(1,items.length);
stashArr = stashArr.slice(1,stashArr.length);
}
newItem = $select.search+' '+$select.taggingLabel;
if ( _findApproxDupe($select.selected, $select.search) > -1 ) {
return;
}
// verify the the tag doesn't match the value of an existing item from
// the searched data set or the items already selected
if ( _findCaseInsensitiveDupe(stashArr.concat($select.selected)) ) {
// if there is a tag from prev iteration, strip it / queue the change
// and return early
if ( hasTag ) {
items = stashArr;
scope.$evalAsync( function () {
$select.activeIndex = 0;
$select.items = items;
});
}
return;
}
if ( _findCaseInsensitiveDupe(stashArr) ) {
// if there is a tag from prev iteration, strip it
if ( hasTag ) {
$select.items = stashArr.slice(1,stashArr.length);
}
return;
}
}
if ( hasTag ) dupeIndex = _findApproxDupe($select.selected, newItem);
// dupe found, shave the first item
if ( dupeIndex > -1 ) {
items = items.slice(dupeIndex+1,items.length-1);
} else {
items = [];
if (newItem) items.push(newItem);
items = items.concat(stashArr);
}
scope.$evalAsync( function () {
$select.activeIndex = 0;
$select.items = items;
if ($select.isGrouped) {
// update item references in groups, so that indexOf will work after angular.copy
var itemsWithoutTag = newItem ? items.slice(1) : items;
$select.setItemsFn(itemsWithoutTag);
if (newItem) {
// add tag item as a new group
$select.items.unshift(newItem);
$select.groups.unshift({name: '', items: [newItem], tagging: true});
}
}
});
}
});
function _findCaseInsensitiveDupe(arr) {
if ( arr === undefined || $select.search === undefined ) {
return false;
}
var hasDupe = arr.filter( function (origItem) {
if ( $select.search.toUpperCase() === undefined || origItem === undefined ) {
return false;
}
return origItem.toUpperCase() === $select.search.toUpperCase();
}).length > 0;
return hasDupe;
}
function _findApproxDupe(haystack, needle) {
var dupeIndex = -1;
if(angular.isArray(haystack)) {
var tempArr = angular.copy(haystack);
for (var i = 0; i <tempArr.length; i++) {
// handle the simple string version of tagging
if ( $select.tagging.fct === undefined ) {
// search the array for the match
if ( tempArr[i]+' '+$select.taggingLabel === needle ) {
dupeIndex = i;
}
// handle the object tagging implementation
} else {
var mockObj = tempArr[i];
if (angular.isObject(mockObj)) {
mockObj.isTag = true;
}
if ( angular.equals(mockObj, needle) ) {
dupeIndex = i;
}
}
}
}
return dupeIndex;
}
$select.searchInput.on('blur', function() {
$timeout(function() {
$selectMultiple.activeMatchIndex = -1;
});
});
}
};
}]);

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше