├── .bowerrc ├── .editorconfig ├── .ember-cli ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── .watchmanconfig ├── Brocfile.js ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── addon ├── .gitkeep ├── helpers │ └── ivy-sortable.js ├── index.js └── views │ └── ivy-sortable.js ├── app ├── .gitkeep ├── helpers │ └── ivy-sortable.js └── views │ └── ivy-sortable.js ├── blueprints └── ivy-sortable │ └── index.js ├── bower.json ├── config ├── ember-try.js └── environment.js ├── index.js ├── package.json ├── publish-build.sh ├── standalone ├── Brocfile.js ├── glue.js └── package_manager_files │ └── bower.json ├── testem.json ├── tests ├── .jshintrc ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── index.js │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ ├── templates │ │ │ ├── application.hbs │ │ │ ├── components │ │ │ │ └── .gitkeep │ │ │ └── index.hbs │ │ └── views │ │ │ └── .gitkeep │ ├── config │ │ └── environment.js │ └── public │ │ ├── .gitkeep │ │ ├── crossdomain.xml │ │ └── robots.txt ├── helpers │ ├── resolver.js │ └── start-app.js ├── index.html ├── test-helper.js └── unit │ ├── .gitkeep │ └── views │ └── ivy-sortable-test.js └── vendor └── .gitkeep /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components", 3 | "analytics": false 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.js] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.hbs] 21 | insert_final_newline = false 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [*.css] 26 | indent_style = space 27 | indent_size = 2 28 | 29 | [*.html] 30 | indent_style = space 31 | indent_size = 2 32 | 33 | [*.{diff,md}] 34 | trim_trailing_whitespace = false 35 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /standalone/dist 6 | /tmp 7 | 8 | # dependencies 9 | /node_modules 10 | /bower_components 11 | 12 | # misc 13 | /.sass-cache 14 | /connect.lock 15 | /coverage/* 16 | /libpeerconnection.log 17 | npm-debug.log 18 | testem.log 19 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "-Promise" 6 | ], 7 | "browser": true, 8 | "boss": true, 9 | "curly": true, 10 | "debug": false, 11 | "devel": true, 12 | "eqeqeq": true, 13 | "evil": true, 14 | "forin": false, 15 | "immed": false, 16 | "laxbreak": false, 17 | "newcap": true, 18 | "noarg": true, 19 | "noempty": false, 20 | "nonew": false, 21 | "nomen": false, 22 | "onevar": false, 23 | "plusplus": false, 24 | "regexp": false, 25 | "undef": true, 26 | "sub": true, 27 | "strict": false, 28 | "white": false, 29 | "eqnull": true, 30 | "esnext": true, 31 | "unused": true 32 | } 33 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components/ 2 | tests/ 3 | tmp/ 4 | dist/ 5 | 6 | .bowerrc 7 | .editorconfig 8 | .ember-cli 9 | .travis.yml 10 | .npmignore 11 | **/.gitkeep 12 | bower.json 13 | Brocfile.js 14 | testem.json 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | - "0.12" 5 | 6 | sudo: false 7 | 8 | after_success: 9 | - npm run-script publish-build 10 | 11 | cache: 12 | directories: 13 | - node_modules 14 | 15 | env: 16 | global: 17 | - secure: quspYgz+EPvSa5V5ekOZiendjzfq6G8PhTvtzKUCJou/GsaBvtcydc+9HAdTH9ZDQmuGTK7JHtDN0cv+iW5O6sca89brDEk8Si9B+R1QBPMsf1neykudjuXJiR3vo4E/moRXptBuRSwkENh396+bHzjQbMLx0NM8Zdemi4LNmW8= 18 | matrix: 19 | - EMBER_TRY_SCENARIO=default 20 | - EMBER_TRY_SCENARIO=ember-release 21 | - EMBER_TRY_SCENARIO=ember-beta 22 | - EMBER_TRY_SCENARIO=ember-canary 23 | 24 | matrix: 25 | fast_finish: true 26 | allow_failures: 27 | - env: EMBER_TRY_SCENARIO=ember-canary 28 | 29 | before_install: 30 | - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH 31 | - "npm config set spin false" 32 | - "npm install -g npm@^2" 33 | 34 | install: 35 | - npm install -g bower 36 | - npm install 37 | - bower install 38 | 39 | script: 40 | - ember try $EMBER_TRY_SCENARIO test 41 | 42 | notifications: 43 | email: false 44 | slack: 45 | on_success: change 46 | on_failure: always 47 | rooms: 48 | - secure: bVqgdqmTdZoQ7cvzKVYC0/WpZVhhDorEURioBXyPqao0Pb1c8wHNZ6OmqNfbbns/H2VkZEZ/F0rPtUc65T5XtEaPxE75wcTKe6oBzpb8Xaea2byIdADcCLD5YKBKZXQyK5olgDzwhdkVRg13Ro01Q/gVmdi0MZAZtmKMZFq+tMw= 49 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp"] 3 | } 4 | -------------------------------------------------------------------------------- /Brocfile.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | /* global require, module */ 3 | 4 | var EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 5 | var pickFiles = require('broccoli-static-compiler'); 6 | 7 | /* 8 | This Brocfile specifes the options for the dummy test app of this 9 | addon, located in `/tests/dummy` 10 | 11 | This Brocfile does *not* influence how the addon or the app using it 12 | behave. You most likely want to be modifying `./index.js` or app's Brocfile 13 | */ 14 | 15 | var app = new EmberAddon(); 16 | 17 | app.import(app.bowerDirectory + '/ember/ember-template-compiler.js', { type: 'test' }); 18 | app.import(app.bowerDirectory + '/jquery-simulate/jquery.simulate.js', { type: 'test' }); 19 | 20 | var bootstrapTree = pickFiles(app.bowerDirectory + '/bootstrap/dist', { 21 | destDir: '/assets', 22 | srcDir: '/' 23 | }); 24 | 25 | module.exports = app.toTree([bootstrapTree]); 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ivy-sortable 2 | 3 | ## 1.0.0-beta.5 4 | 5 | * Add `handle` option. [@ToddSmithSalter](https://github.com/ToddSmithSalter) 6 | * Upgrade to ember-cli 0.2.7. 7 | * Fix reference to `_parentView`, so action triggering works in Ember 1.13+. 8 | * Pull jQuery UI options into a `uiOptions` concatenated property. 9 | 10 | ## 1.0.0-beta.4 11 | 12 | * Add repo URL to package.json. 13 | * Fix potential Morph errors when the content array is modified. 14 | 15 | ## 1.0.0-beta.3 16 | 17 | * Deprecate the `{{ivy-sortable}}` helper. 18 | 19 | ## 1.0.0-beta.2 20 | 21 | * Check `isDestroying` before refreshing the sortable. 22 | 23 | ## 1.0.0-beta.1 24 | 25 | * Call sortable's `refresh` method after content changes. This fixes an issue 26 | where the "ui-sortable-handle" class would not be added to newly-inserted 27 | list items. See [#2](https://github.com/IvyApp/ivy-sortable/issues/2) for 28 | further details. 29 | 30 | ## 0.3.0 31 | 32 | * Remove version suffix from ivy-sortable.js. 33 | 34 | ## 0.2.1 35 | 36 | * Add jQuery UI Sortable option bindings. 37 | * Upgrade to ember-cli 0.1.5. 38 | * Remove unnecessary ember-data dependency in dummy app. 39 | * Upgrade to ember 1.9.1 in dummy app. 40 | 41 | ## 0.2.0 42 | 43 | * Convert to an ember-cli addon. 44 | 45 | ## 0.1.0 46 | 47 | * Initial release. 48 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Envy Labs, LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ivy-sortable 2 | 3 | [![Build Status](https://travis-ci.org/IvyApp/ivy-sortable.svg?branch=master)](https://travis-ci.org/IvyApp/ivy-sortable) 4 | 5 | An [Ember](http://emberjs.com) component for [jQuery UI's Sortable Widget](http://jqueryui.com/sortable/). 6 | 7 | ## Installation 8 | 9 | As an [ember-cli](http://www.ember-cli.com/) addon: 10 | 11 | ```sh 12 | npm install --save-dev ivy-sortable 13 | ember generate ivy-sortable 14 | ``` 15 | 16 | Or if you aren't using ember-cli, you can use this library as a standalone [Bower](http://bower.io/) package: 17 | 18 | ```sh 19 | bower install --save ivy-sortable 20 | ``` 21 | 22 | ...and then add the `ivy-sortable.js` script to your page. 23 | 24 | ## Usage 25 | 26 | Use the `ivy-sortable` view with Ember's built-in `collection` helper. 27 | 28 | ```handlebars 29 | {{#collection "ivy-sortable" content=people}} 30 | Greetings, {{name}}! 31 | {{/collection}} 32 | ``` 33 | 34 | This will output a sortable list, and dragging and dropping items will reorder them in the corresponding array (in this case, `people`). There is also a `moved` action that will fire after an item has been moved, in case you want to take further action: 35 | 36 | ```handlebars 37 | {{#collection content=people moved="movePerson"}} 38 | Greetings, {{name}}! 39 | {{/collection}} 40 | ``` 41 | 42 | In this case the `movePerson` handler will be called with the item that has been moved, and its old and new index: 43 | 44 | ```js 45 | App.ApplicationController = Ember.Controller.extend({ 46 | actions: { 47 | movePerson: function(person, oldIndex, newIndex) { 48 | // ... 49 | } 50 | } 51 | }); 52 | ``` 53 | 54 | The following [jQuery UI Sortable options](http://api.jqueryui.com/sortable/#options) are supported: 55 | 56 | * `axis` 57 | * `containment` 58 | * `cursorAt` 59 | * `cursor` 60 | * `delay` 61 | * `disabled` 62 | * `distance` 63 | * `forceHelperSize` 64 | * `forcePlaceholderSize` 65 | * `grid` 66 | * `handle` 67 | * `helper` 68 | * `opacity` 69 | * `placeholder` 70 | * `revert` 71 | * `scrollSensitivity` 72 | * `scrollSpeed` 73 | * `scroll` 74 | * `tolerance` 75 | * `zIndex` 76 | 77 | If there's an option that you need that isn't supported, you can easily add it in your own app by extending from 'ivy-sortable/views/ivy-sortable' and adding it to the `uiOptions` property like so: 78 | 79 | ```js 80 | import IvySortableView from 'ivy-sortable/views/ivy-sortable'; 81 | 82 | export default IvySortableView.extend({ 83 | uiOptions: [ 84 | 'someOtherOption' 85 | ] 86 | }); 87 | ``` 88 | 89 | ## Contributing 90 | 91 | Fork this repo, make a new branch, and send a pull request. Make sure your change is tested or it won't be merged. 92 | 93 | To run tests: 94 | 95 | ```sh 96 | git clone # 97 | npm install 98 | npm test 99 | ``` 100 | 101 | Or, to start a test server that continually runs (for development): 102 | 103 | ```sh 104 | ember test --server 105 | ``` 106 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/addon/.gitkeep -------------------------------------------------------------------------------- /addon/helpers/ivy-sortable.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default function(path, options) { 4 | var ctx, helperName = 'ivy-sortable'; 5 | 6 | Ember.deprecate( 7 | 'Using the {{ivy-sortable}} helper is deprecated. ' + 8 | 'Please use the collection form ' + 9 | '(`{{#collection "ivy-sortable" content=foo}}`) instead.'); 10 | 11 | if (arguments.length === 1) { 12 | options = path; 13 | path = 'this'; 14 | } else { 15 | helperName += ' ' + path; 16 | } 17 | 18 | options.hash.contentBinding = path; 19 | 20 | // can't rely on this default behavior when use strict 21 | ctx = this || window; 22 | 23 | options.helperName = options.helperName || helperName; 24 | 25 | return Ember.Handlebars.helpers.collection.call(ctx, 'ivy-sortable', options); 26 | } 27 | -------------------------------------------------------------------------------- /addon/index.js: -------------------------------------------------------------------------------- 1 | import IvySortableView from './views/ivy-sortable'; 2 | import ivySortableHelper from './helpers/ivy-sortable'; 3 | 4 | export { 5 | IvySortableView, 6 | ivySortableHelper 7 | }; 8 | -------------------------------------------------------------------------------- /addon/views/ivy-sortable.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.CollectionView.extend(Ember.TargetActionSupport, { 4 | disabled: false, 5 | 6 | tagName: 'ul', 7 | 8 | concatenatedProperties: ['uiOptions'], 9 | 10 | uiOptions: [ 11 | 'axis', 12 | 'containment', 13 | 'cursor', 14 | 'cursorAt', 15 | 'delay', 16 | 'disabled', 17 | 'distance', 18 | 'forceHelperSize', 19 | 'forcePlaceholderSize', 20 | 'grid', 21 | 'handle', 22 | 'helper', 23 | 'opacity', 24 | 'placeholder', 25 | 'revert', 26 | 'scroll', 27 | 'scrollSensitivity', 28 | 'scrollSpeed', 29 | 'tolerance', 30 | 'zIndex' 31 | ], 32 | 33 | arrayDidChangeAfterElementInserted: function() { 34 | Ember.run.scheduleOnce('afterRender', this, this._refreshSortable); 35 | }, 36 | 37 | destroySortable: Ember.on('willDestroyElement', function() { 38 | this._contentWillChangeAfterElementInserted(); 39 | 40 | Ember.removeBeforeObserver(this, 'content', this, this._contentWillChangeAfterElementInserted); 41 | this.removeObserver('content', this, this._contentDidChangeAfterElementInserted); 42 | 43 | this.$().sortable('destroy'); 44 | }), 45 | 46 | initSortable: Ember.on('didInsertElement', function() { 47 | var opts = {}; 48 | 49 | Ember.EnumerableUtils.forEach(['start', 'stop'], function(callback) { 50 | opts[callback] = Ember.run.bind(this, callback); 51 | }, this); 52 | 53 | this.$().sortable(opts); 54 | 55 | Ember.EnumerableUtils.forEach(this.get('uiOptions'), this._bindSortableOption, this); 56 | 57 | Ember.addBeforeObserver(this, 'content', this, this._contentWillChangeAfterElementInserted); 58 | this.addObserver('content', this, this._contentDidChangeAfterElementInserted); 59 | 60 | this._contentDidChangeAfterElementInserted(); 61 | }), 62 | 63 | move: function(oldIndex, newIndex) { 64 | var content = this.get('content'); 65 | 66 | if (content) { 67 | var item = content.objectAt(oldIndex); 68 | 69 | content.removeAt(oldIndex); 70 | content.insertAt(newIndex, item); 71 | 72 | this.sendAction('moved', item, oldIndex, newIndex); 73 | } 74 | }, 75 | 76 | /** 77 | * Copied from `Ember.Component`. Read the `sendAction` documentation there 78 | * for more information. 79 | * 80 | * @method sendAction 81 | */ 82 | sendAction: function(action) { 83 | var actionName; 84 | var contexts = Array.prototype.slice.call(arguments, 1); 85 | 86 | if (action === undefined) { 87 | actionName = this.get('action'); 88 | Ember.assert('The default action was triggered on the component ' + this.toString() + 89 | ', but the action name (' + actionName + ') was not a string.', 90 | Ember.isNone(actionName) || typeof actionName === 'string'); 91 | } else { 92 | actionName = this.get(action); 93 | Ember.assert('The ' + action + ' action was triggered on the component ' + 94 | this.toString() + ', but the action name (' + actionName + 95 | ') was not a string.', 96 | Ember.isNone(actionName) || typeof actionName === 'string'); 97 | } 98 | 99 | // If no action name for that action could be found, just abort. 100 | if (actionName === undefined) { return; } 101 | 102 | this.triggerAction({ 103 | action: actionName, 104 | actionContext: contexts 105 | }); 106 | }, 107 | 108 | start: function(event, ui) { 109 | ui.item.data('oldIndex', ui.item.index()); 110 | }, 111 | 112 | stop: function(event, ui) { 113 | var oldIndex = ui.item.data('oldIndex'); 114 | var newIndex = ui.item.index(); 115 | 116 | // Ember.CollectionView wants to control the DOM, so make sure jQuery UI 117 | // isn't moving elements around without it knowing. Calling cancel here 118 | // will revert the item to its prior position. 119 | this.$().sortable('cancel'); 120 | 121 | this.move(oldIndex, newIndex); 122 | }, 123 | 124 | targetObject: Ember.computed(function() { 125 | var parentView = this.get('parentView'); 126 | return parentView ? parentView.get('controller') : null; 127 | }).property('parentView'), 128 | 129 | _bindSortableOption: function(key) { 130 | this.addObserver(key, this, this._optionDidChange); 131 | 132 | if (key in this) { 133 | this._optionDidChange(this, key); 134 | } 135 | 136 | this.on('willDestroyElement', this, function() { 137 | this.removeObserver(key, this, this._optionDidChange); 138 | }); 139 | }, 140 | 141 | _contentDidChangeAfterElementInserted: function() { 142 | var content = this.get('content'); 143 | 144 | if (content) { 145 | content.addArrayObserver(this, { 146 | didChange: 'arrayDidChangeAfterElementInserted' 147 | }); 148 | } 149 | 150 | var len = content ? Ember.get(content, 'length') : 0; 151 | this.arrayDidChangeAfterElementInserted(content, 0, null, len); 152 | }, 153 | 154 | _contentWillChangeAfterElementInserted: function() { 155 | var content = this.get('content'); 156 | 157 | if (content) { 158 | content.removeArrayObserver(this, { 159 | didChange: 'arrayDidChangeAfterElementInserted' 160 | }); 161 | } 162 | }, 163 | 164 | _optionDidChange: function(sender, key) { 165 | this.$().sortable('option', key, this.get(key)); 166 | }, 167 | 168 | _refreshSortable: function() { 169 | if (this.isDestroying) { return; } 170 | this.$().sortable('refresh'); 171 | } 172 | }); 173 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/app/.gitkeep -------------------------------------------------------------------------------- /app/helpers/ivy-sortable.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ivy-sortable/helpers/ivy-sortable'; 2 | -------------------------------------------------------------------------------- /app/views/ivy-sortable.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ivy-sortable/views/ivy-sortable'; 2 | -------------------------------------------------------------------------------- /blueprints/ivy-sortable/index.js: -------------------------------------------------------------------------------- 1 | /* jshint node:true */ 2 | 3 | module.exports = { 4 | afterInstall: function() { 5 | return this.addBowerPackageToProject('jquery-ui'); 6 | }, 7 | 8 | normalizeEntityName: function() { 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ivy-sortable", 3 | "dependencies": { 4 | "ember": "1.12.0", 5 | "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3", 6 | "ember-cli-test-loader": "ember-cli-test-loader#0.1.3", 7 | "ember-load-initializers": "ember-cli/ember-load-initializers#0.1.4", 8 | "ember-qunit": "0.3.3", 9 | "ember-qunit-notifications": "0.0.7", 10 | "ember-resolver": "~0.1.15", 11 | "jquery": "^1.11.1", 12 | "jquery-ui": "~1.11.2", 13 | "jquery-simulate": "~1.0.0", 14 | "bootstrap": "~3.3.1", 15 | "loader.js": "ember-cli/loader.js#3.2.0", 16 | "qunit": "~1.17.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | scenarios: [ 3 | { 4 | name: 'default', 5 | dependencies: { } 6 | }, 7 | { 8 | name: 'ember-release', 9 | dependencies: { 10 | 'ember': 'components/ember#release' 11 | }, 12 | resolutions: { 13 | 'ember': 'release' 14 | } 15 | }, 16 | { 17 | name: 'ember-beta', 18 | dependencies: { 19 | 'ember': 'components/ember#beta' 20 | }, 21 | resolutions: { 22 | 'ember': 'beta' 23 | } 24 | }, 25 | { 26 | name: 'ember-canary', 27 | dependencies: { 28 | 'ember': 'components/ember#canary' 29 | }, 30 | resolutions: { 31 | 'ember': 'canary' 32 | } 33 | } 34 | ] 35 | }; 36 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(/* environment, appConfig */) { 4 | return { }; 5 | }; 6 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | name: 'ivy-sortable', 6 | 7 | included: function(app) { 8 | app.import(app.bowerDirectory + '/jquery-ui/ui/core.js'); 9 | app.import(app.bowerDirectory + '/jquery-ui/ui/widget.js'); 10 | app.import(app.bowerDirectory + '/jquery-ui/ui/mouse.js'); 11 | app.import(app.bowerDirectory + '/jquery-ui/ui/sortable.js'); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ivy-sortable", 3 | "version": "1.0.0-beta.6+canary", 4 | "description": "Ember helper for interacting with jQuery UI's Sortable module.", 5 | "directories": { 6 | "doc": "doc", 7 | "test": "tests" 8 | }, 9 | "scripts": { 10 | "publish-build": "./publish-build.sh", 11 | "start": "ember server", 12 | "build": "ember build", 13 | "test": "ember try:testall" 14 | }, 15 | "repository": "https://github.com/IvyApp/ivy-sortable", 16 | "engines": { 17 | "node": ">= 0.10.0" 18 | }, 19 | "author": "", 20 | "license": "MIT", 21 | "devDependencies": { 22 | "broccoli": "^0.13.3", 23 | "broccoli-ember-auto-register": "^0.2.0", 24 | "broccoli-es3-safe-recast": "^1.0.0", 25 | "broccoli-es6-concatenator": "^0.1.11", 26 | "broccoli-iife": "^0.1.0", 27 | "broccoli-merge-trees": "^0.2.1", 28 | "broccoli-replace": "^0.2.0", 29 | "broccoli-static-compiler": "^0.2.1", 30 | "broccoli-asset-rev": "^2.0.2", 31 | "ember-cli": "0.2.7", 32 | "ember-cli-app-version": "0.3.3", 33 | "ember-cli-content-security-policy": "0.4.0", 34 | "ember-cli-dependency-checker": "^1.0.0", 35 | "ember-cli-htmlbars": "0.7.6", 36 | "ember-cli-ic-ajax": "0.1.1", 37 | "ember-cli-inject-live-reload": "^1.3.0", 38 | "ember-cli-qunit": "0.3.13", 39 | "ember-cli-uglify": "^1.0.1", 40 | "ember-disable-proxy-controllers": "^1.0.0", 41 | "ember-export-application-global": "^1.0.2", 42 | "ember-disable-prototype-extensions": "^1.0.0", 43 | "ember-try": "0.0.6" 44 | }, 45 | "keywords": [ 46 | "ember-addon" 47 | ], 48 | "dependencies": { 49 | "ember-cli-babel": "^5.0.0" 50 | }, 51 | "ember-addon": { 52 | "configPath": "tests/dummy/config" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /publish-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Building standalone library..." 4 | cd standalone 5 | rm -rf dist 6 | ../node_modules/.bin/broccoli build dist 7 | 8 | git config --global user.email "dray@envylabs.com" 9 | git config --global user.name "Dray Lacy" 10 | 11 | # The repository we are going to work with. 12 | COMPONENTS_REPO_SLUG="IvyApp/components-ivy-sortable" 13 | 14 | # The user who is associated with GH_TOKEN. 15 | USER="omghax" 16 | 17 | echo -e "COMPONENTS_REPO_SLUG: ${COMPONENTS_REPO_SLUG}\n" 18 | echo -e "CURRENT_BRANCH: ${TRAVIS_BRANCH}\n" 19 | 20 | # Set channel to publish to. If no suitable branch is found exit successfully. 21 | case $TRAVIS_BRANCH in 22 | "master" ) 23 | CHANNEL="canary" ;; 24 | "beta" ) 25 | CHANNEL="beta" ;; 26 | "stable" ) 27 | CHANNEL="release" ;; 28 | "release" ) 29 | CHANNEL="release" ;; 30 | * ) 31 | echo "Not a bower release branch. Exiting!" 32 | exit 0 ;; 33 | esac 34 | echo -e "CHANNEL: ${CHANNEL}\n" 35 | 36 | # Sending output to /dev/null to prevent GH_TOKEN leak on error. 37 | git clone --branch ${CHANNEL} https://${USER}:${GH_TOKEN}@github.com/${COMPONENTS_REPO_SLUG}.git bower_ivy_sortable &> /dev/null 38 | rm -rf bower_ivy_sortable/* 39 | cp -R dist/* bower_ivy_sortable/ 40 | cd bower_ivy_sortable 41 | git remote rm origin 42 | 43 | # Sending output to /dev/null to prevent GH_TOKEN leak on error. 44 | git remote add origin https://${USER}:${GH_TOKEN}@github.com/${COMPONENTS_REPO_SLUG}.git &> /dev/null 45 | git add -A 46 | git commit -m "Bower auto-build for https://github.com/IvyApp/ivy-sortable/commits/${TRAVIS_COMMIT}." 47 | 48 | # Sending output to /dev/null to prevent GH_TOKEN leak on error. 49 | git push -fq origin ${CHANNEL} &> /dev/null 50 | echo -e "Done\n" 51 | -------------------------------------------------------------------------------- /standalone/Brocfile.js: -------------------------------------------------------------------------------- 1 | /* jshint node:true */ 2 | 3 | var autoRegister = require('broccoli-ember-auto-register'); 4 | var compileES6 = require('broccoli-es6-concatenator'); 5 | var es3Safe = require('broccoli-es3-safe-recast'); 6 | var iife = require('broccoli-iife'); 7 | var mergeTrees = require('broccoli-merge-trees'); 8 | var packageInfo = require('../package.json'); 9 | var pickFiles = require('broccoli-static-compiler'); 10 | var replace = require('broccoli-replace'); 11 | 12 | var addonTree = pickFiles('../addon', { 13 | destDir: packageInfo.name, 14 | files: ['**/*.js'], 15 | srcDir: '/' 16 | }); 17 | 18 | var registryTree = pickFiles(addonTree, { 19 | destDir: '/', 20 | files: ['{helpers,views}/**/*.js'], 21 | srcDir: packageInfo.name 22 | }); 23 | registryTree = autoRegister(registryTree, { 24 | moduleName: packageInfo.name + '-shim', 25 | modulePrefix: packageInfo.name, 26 | outputFile: 'registry.js' 27 | }); 28 | 29 | var loaderTree = pickFiles('../bower_components', { 30 | destDir: '/', 31 | files: ['loader.js'], 32 | srcDir: '/loader.js' 33 | }); 34 | 35 | var glueTree = pickFiles('.', { 36 | destDir: '/', 37 | files: ['glue.js'], 38 | srcDir: '/' 39 | }); 40 | 41 | var jsTree = mergeTrees([addonTree, glueTree, loaderTree, registryTree]); 42 | 43 | var es6Tree = compileES6(jsTree, { 44 | ignoredModules: ['ember'], 45 | inputFiles: [packageInfo.name + '/**/*.js'], 46 | legacyFilesToAppend: ['registry.js', 'glue.js'], 47 | loaderFile: 'loader.js', 48 | outputFile: '/' + packageInfo.name + '.js', 49 | wrapInEval: false 50 | }); 51 | es6Tree = es3Safe(es6Tree); 52 | es6Tree = iife(es6Tree); 53 | 54 | var packagingTree = pickFiles('package_manager_files', { 55 | destDir: '/', 56 | srcDir: '/' 57 | }); 58 | 59 | var distTree = mergeTrees([es6Tree, packagingTree]); 60 | distTree = replace(distTree, { 61 | files: ['**/*.js', '**/*.json'], 62 | patterns: [ 63 | { match: /VERSION_STRING_PLACEHOLDER/g, replacement: packageInfo.version } 64 | ] 65 | }); 66 | 67 | module.exports = distTree; 68 | -------------------------------------------------------------------------------- /standalone/glue.js: -------------------------------------------------------------------------------- 1 | /* global define, require, window */ 2 | var addonName = 'ivy-sortable'; 3 | 4 | define('ember', ['exports'], function(__exports__) { 5 | __exports__['default'] = window.Ember; 6 | }); 7 | 8 | var index = addonName + '/index'; 9 | define(addonName, ['exports'], function(__exports__) { 10 | var library = require(index); 11 | Object.keys(library).forEach(function(key) { 12 | __exports__[key] = library[key]; 13 | }); 14 | }); 15 | 16 | // Glue library to a global var 17 | window.IvySortable = require(index); 18 | 19 | // Register library items in the container 20 | var shim = addonName + '-shim'; 21 | window.Ember.Application.initializer({ 22 | name: shim, 23 | 24 | initialize: function(container) { 25 | require(shim)['default'](container); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /standalone/package_manager_files/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ivy-sortable", 3 | "version": "VERSION_STRING_PLACEHOLDER", 4 | "authors": [ 5 | "Dray Lacy" 6 | ], 7 | "description": "An Ember component for jQuery UI's Sortable Widget.", 8 | "main": "./ivy-sortable.js", 9 | "keywords": [ 10 | "ember" 11 | ], 12 | "dependencies": { 13 | "ember": ">= 1.3.0", 14 | "jquery-ui": "~> 1.11.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /testem.json: -------------------------------------------------------------------------------- 1 | { 2 | "framework": "qunit", 3 | "test_page": "tests/index.html?hidepassed", 4 | "disable_watching": true, 5 | "launch_in_ci": [ 6 | "PhantomJS" 7 | ], 8 | "launch_in_dev": [ 9 | "PhantomJS", 10 | "Chrome" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tests/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "location", 6 | "setTimeout", 7 | "$", 8 | "-Promise", 9 | "define", 10 | "console", 11 | "visit", 12 | "exists", 13 | "fillIn", 14 | "click", 15 | "keyEvent", 16 | "triggerEvent", 17 | "find", 18 | "findWithAssert", 19 | "wait", 20 | "DS", 21 | "andThen", 22 | "currentURL", 23 | "currentPath", 24 | "currentRouteName" 25 | ], 26 | "node": false, 27 | "browser": false, 28 | "boss": true, 29 | "curly": false, 30 | "debug": false, 31 | "devel": false, 32 | "eqeqeq": true, 33 | "evil": true, 34 | "forin": false, 35 | "immed": false, 36 | "laxbreak": false, 37 | "newcap": true, 38 | "noarg": true, 39 | "noempty": false, 40 | "nonew": false, 41 | "nomen": false, 42 | "onevar": false, 43 | "plusplus": false, 44 | "regexp": false, 45 | "undef": true, 46 | "sub": true, 47 | "strict": false, 48 | "white": false, 49 | "eqnull": true, 50 | "esnext": true 51 | } 52 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from 'ember/resolver'; 3 | import loadInitializers from 'ember/load-initializers'; 4 | import config from './config/environment'; 5 | 6 | var App; 7 | 8 | Ember.MODEL_FACTORY_INJECTIONS = true; 9 | 10 | App = Ember.Application.extend({ 11 | modulePrefix: config.modulePrefix, 12 | podModulePrefix: config.podModulePrefix, 13 | Resolver: Resolver 14 | }); 15 | 16 | loadInitializers(App, config.modulePrefix); 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/index.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.ArrayController.extend({ 4 | model: Ember.A([ 5 | Ember.Object.create({ title: 'The Phantom Menace' }), 6 | Ember.Object.create({ title: 'Attack of the Clones' }), 7 | Ember.Object.create({ title: 'Revenge of the Sith' }), 8 | Ember.Object.create({ title: 'A New Hope' }), 9 | Ember.Object.create({ title: 'The Empire Strikes Back' }), 10 | Ember.Object.create({ title: 'Return of the Jedi' }), 11 | Ember.Object.create({ title: 'The Force Awakens' }) 12 | ]) 13 | }); 14 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ivy-sortable 7 | 8 | 9 | 10 | {{content-for 'head'}} 11 | 12 | 13 | 14 | 15 | 16 | {{content-for 'head-footer'}} 17 | 18 | 19 | {{content-for 'body'}} 20 | 21 | 22 | 23 | 24 | {{content-for 'body-footer'}} 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import config from './config/environment'; 3 | 4 | var Router = Ember.Router.extend({ 5 | location: config.locationType 6 | }); 7 | 8 | Router.map(function() { 9 | }); 10 | 11 | export default Router; 12 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | .sortable-item { 2 | cursor: move; 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{outlet}} 3 |
4 |

View source on GitHub.

5 |
6 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/tests/dummy/app/templates/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/templates/index.hbs: -------------------------------------------------------------------------------- 1 |

ivy-sortable

2 | 3 |
4 |
5 |

Drag the items below to rearrange them.

6 | 7 | {{#collection "ivy-sortable" class="list-group" content=model cursor="move" itemClassNames="list-group-item sortable-item"}} 8 | {{title}} 9 | {{/collection}} 10 |
11 |
12 |

The list is:

13 | 14 |
    15 | {{#each movie in this}} 16 |
  1. {{movie.title}}
  2. 17 | {{/each}} 18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /tests/dummy/app/views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/tests/dummy/app/views/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | modulePrefix: 'dummy', 6 | environment: environment, 7 | baseURL: '/', 8 | locationType: 'auto', 9 | contentSecurityPolicy: { 10 | 'style-src': "'self' 'unsafe-inline'" 11 | }, 12 | EmberENV: { 13 | FEATURES: { 14 | // Here you can enable experimental features on an ember canary build 15 | // e.g. 'with-controller': true 16 | } 17 | }, 18 | 19 | APP: { 20 | // Here you can pass flags/options to your application instance 21 | // when it is created 22 | } 23 | }; 24 | 25 | if (environment === 'development') { 26 | // ENV.APP.LOG_RESOLVER = true; 27 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 28 | // ENV.APP.LOG_TRANSITIONS = true; 29 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 30 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 31 | } 32 | 33 | if (environment === 'test') { 34 | // Testem prefers this... 35 | ENV.baseURL = '/'; 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | } 44 | 45 | if (environment === 'production') { 46 | 47 | } 48 | 49 | return ENV; 50 | }; 51 | -------------------------------------------------------------------------------- /tests/dummy/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/tests/dummy/public/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember/resolver'; 2 | import config from '../../config/environment'; 3 | 4 | var resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Application from '../../app'; 3 | import Router from '../../router'; 4 | import config from '../../config/environment'; 5 | 6 | export default function startApp(attrs) { 7 | var application; 8 | 9 | var attributes = Ember.merge({}, config.APP); 10 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; 11 | 12 | Ember.run(function() { 13 | application = Application.create(attributes); 14 | application.setupForTesting(); 15 | application.injectTestHelpers(); 16 | }); 17 | 18 | return application; 19 | } 20 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ivy-sortable Tests 7 | 8 | 9 | 10 | {{content-for 'head'}} 11 | {{content-for 'test-head'}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for 'head-footer'}} 18 | {{content-for 'test-head-footer'}} 19 | 20 | 21 | 22 | {{content-for 'body'}} 23 | {{content-for 'test-body'}} 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{content-for 'body-footer'}} 31 | {{content-for 'test-body-footer'}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { 3 | setResolver 4 | } from 'ember-qunit'; 5 | 6 | setResolver(resolver); 7 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/tests/unit/.gitkeep -------------------------------------------------------------------------------- /tests/unit/views/ivy-sortable-test.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { moduleFor, test } from 'ember-qunit'; 3 | 4 | var people, containerView, view; 5 | 6 | moduleFor('view:ivy-sortable', 'view:ivy-sortable', { 7 | setup: function() { 8 | people = Ember.A([ 9 | { name: 'Tyrion' }, 10 | { name: 'Cersei' } 11 | ]); 12 | 13 | view = this.subject({ 14 | content: people, 15 | 16 | moved: 'moved', 17 | 18 | itemViewClass: Ember.View.extend({ 19 | template: Ember.Handlebars.compile('{{view.content.name}}') 20 | }) 21 | }); 22 | 23 | containerView = Ember.ContainerView.create(); 24 | 25 | Ember.run(function() { 26 | containerView.pushObject(view); 27 | containerView.appendTo('#ember-testing'); 28 | }); 29 | }, 30 | 31 | teardown: function() { 32 | Ember.run(containerView, 'destroy'); 33 | } 34 | }); 35 | 36 | test('should render the template for each item in an array', function(assert) { 37 | var items = view.$('li.ui-sortable-handle'); 38 | assert.equal(items.length, 2); 39 | assert.equal(items.text(), 'TyrionCersei'); 40 | }); 41 | 42 | test('should update the view if content is replaced', function(assert) { 43 | Ember.run(function() { 44 | view.set('content', Ember.A([ 45 | { name: 'Joffrey' }, 46 | { name: 'Sansa' } 47 | ])); 48 | }); 49 | 50 | var items = view.$('li.ui-sortable-handle'); 51 | assert.equal(items.length, 2); 52 | assert.equal(items.text(), 'JoffreySansa'); 53 | }); 54 | 55 | test('should update the view if an item is added', function(assert) { 56 | Ember.run(function() { 57 | people.pushObject({ name: 'Tywin' }); 58 | }); 59 | 60 | assert.equal(view.$().text(), 'TyrionCerseiTywin'); 61 | }); 62 | 63 | test('should update the view if an item is removed', function(assert) { 64 | Ember.run(function() { 65 | people.removeAt(0); 66 | }); 67 | 68 | assert.equal(view.$().text(), 'Cersei'); 69 | }); 70 | 71 | test('should update the view if an item is replaced', function(assert) { 72 | Ember.run(function() { 73 | people.removeAt(0); 74 | people.insertAt(0, { name: 'Jaime' }); 75 | }); 76 | 77 | assert.equal(view.$().text(), 'JaimeCersei'); 78 | }); 79 | 80 | test('should not cause Morph errors if an item is dragged and then moved', function(assert) { 81 | view.$('li:eq(0)').simulate('drag', { dy: 22 }); 82 | 83 | assert.equal(view.$().text(), 'CerseiTyrion'); 84 | 85 | Ember.run(function() { 86 | var tyrion = people.objectAt(1); 87 | people.removeAt(1); 88 | people.insertAt(0, tyrion); 89 | }); 90 | 91 | assert.equal(view.$().text(), 'TyrionCersei'); 92 | }); 93 | 94 | test('should update the view if an item is dragged', function(assert) { 95 | view.$('li:eq(0)').simulate('drag', { dy: 22 }); 96 | 97 | assert.equal(view.$().text(), 'CerseiTyrion'); 98 | }); 99 | 100 | test('should update the content if an item is dragged', function(assert) { 101 | view.$('li:eq(0)').simulate('drag', { dy: 22 }); 102 | 103 | assert.deepEqual(people.mapBy('name'), ['Cersei', 'Tyrion']); 104 | }); 105 | 106 | test('should become disabled if the disabled attribute is true', function(assert) { 107 | Ember.run(function() { view.set('disabled', true); }); 108 | view.$('li:eq(0)').simulate('drag', { dy: 22 }); 109 | 110 | assert.equal(view.$().text(), 'TyrionCersei', 'setting disabled to true disables dragging'); 111 | 112 | Ember.run(function() { view.set('disabled', false); }); 113 | view.$('li:eq(0)').simulate('drag', { dy: 22 }); 114 | 115 | assert.equal(view.$().text(), 'CerseiTyrion', 'setting disabled to false enables dragging'); 116 | }); 117 | 118 | test('should trigger moved action after successful drag', function(assert) { 119 | assert.expect(3); 120 | 121 | Ember.run(function() { 122 | containerView.set('controller', Ember.Object.create({ 123 | moved: function(item, oldIndex, newIndex) { 124 | assert.strictEqual(item, people.objectAt(1)); 125 | assert.equal(oldIndex, 0); 126 | assert.equal(newIndex, 1); 127 | } 128 | })); 129 | }); 130 | 131 | view.$('li:eq(0)').simulate('drag', { dy: 22 }); 132 | }); 133 | 134 | test('should not refresh after destruction', function(assert) { 135 | assert.expect(1); 136 | 137 | Ember.run(function() { 138 | people.pushObject({ name: 'Arya' }); 139 | containerView.destroy(); 140 | }); 141 | 142 | assert.ok(true, 'no error was thrown'); 143 | }); 144 | 145 | function optionTest(key, beforeValue, afterValue) { 146 | test('should update jQuery UI ' + key + ' option when ' + key + ' property changes', function(assert) { 147 | assert.equal(view.$().sortable('option', key), beforeValue, 148 | 'precond - initial value of ' + key + ' option is correct'); 149 | 150 | Ember.run(function() { 151 | view.set(key, afterValue); 152 | }); 153 | 154 | assert.equal(view.$().sortable('option', key), afterValue, 155 | key + ' option is updated after ' + key + ' property is changed'); 156 | }); 157 | } 158 | 159 | optionTest('axis', false, 'x'); 160 | optionTest('containment', false, 'parent'); 161 | optionTest('cursor', 'auto', 'move'); 162 | optionTest('cursorAt', false, { left: 5 }); 163 | optionTest('delay', 0, 150); 164 | optionTest('disabled', false, true); 165 | optionTest('distance', 1, 5); 166 | optionTest('forceHelperSize', false, true); 167 | optionTest('forcePlaceholderSize', false, true); 168 | optionTest('grid', false, [20, 10]); 169 | optionTest('handle', false, '.handle'); 170 | optionTest('helper', 'original', 'clone'); 171 | optionTest('opacity', false, 0.5); 172 | optionTest('placeholder', false, 'sortable-placeholder'); 173 | optionTest('revert', false, true); 174 | optionTest('scroll', true, false); 175 | optionTest('scrollSensitivity', 20, 10); 176 | optionTest('scrollSpeed', 20, 40); 177 | optionTest('tolerance', 'intersect', 'pointer'); 178 | optionTest('zIndex', 1000, 9999); 179 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IvyApp/ivy-sortable/e0ab1681aba92c20885cbb49b778283cdf0c4021/vendor/.gitkeep --------------------------------------------------------------------------------