diff --git a/meteor/.versions b/meteor/.versions deleted file mode 100644 index 777e70e..0000000 --- a/meteor/.versions +++ /dev/null @@ -1,34 +0,0 @@ -base64@1.0.3 -binary-heap@1.0.3 -blaze@2.1.2 -blaze-tools@1.0.3 -callback-hook@1.0.3 -check@1.0.5 -dburles:mongo-collection-instances@0.3.4 -ddp@1.1.0 -deps@1.0.7 -ejson@1.0.6 -geojson-utils@1.0.3 -html-tools@1.0.4 -htmljs@1.0.4 -id-map@1.0.3 -jquery@1.11.3_2 -json@1.0.3 -lai:collection-extensions@0.1.4 -local-test:rubaxa:sortable@1.2.1 -logging@1.0.7 -meteor@1.1.6 -minifiers@1.1.5 -minimongo@1.0.8 -mongo@1.1.0 -observe-sequence@1.0.6 -ordered-dict@1.0.3 -random@1.0.3 -reactive-var@1.0.5 -retry@1.0.3 -rubaxa:sortable@1.2.1 -spacebars-compiler@1.0.6 -templating@1.1.1 -tinytest@1.0.5 -tracker@1.0.7 -underscore@1.0.3 diff --git a/meteor/README.md b/meteor/README.md deleted file mode 100644 index 91d96ad..0000000 --- a/meteor/README.md +++ /dev/null @@ -1,133 +0,0 @@ -Reactive reorderable lists with [Sortable](http://rubaxa.github.io/Sortable/), -backed by [Meteor.js](http://meteor.com) collections: - -* new elements arriving in the collection will update the list as you expect -* elements removed from the collection will be removed from the list -* drag and drop between lists updates collections accordingly - -Demo: http://rubaxa-sortable.meteor.com - -# Meteor - -If you're new to Meteor, here's what the excitement is all about - -[watch the first two minutes](https://www.youtube.com/watch?v=fsi0aJ9yr2o); you'll be hooked by 1:28. -That screencast is from 2012. In the meantime, Meteor has become a mature JavaScript-everywhere web -development framework. Read more at [Why Meteor](http://wiki.dandascalescu.com/essays/why_meteor). - - -# Usage - -Simplest invocation - order will be lost when the page is refreshed: - -```handlebars -{{#sortable }} -``` - -Persist the sort order in the 'order' field of each document in the collection: - -*Client:* - -```handlebars -{{#sortable items= sortField="order"}} -``` - -*Server:* - -```js -Sortable.collections = ; // the name, not the variable -``` - -Along with `items`, `sortField` is the only Meteor-specific option. If it's missing, the package will -assume there is a field called "order" in the collection, holding unique `Number`s such that every -`order` differs from that before and after it by at least 1. Basically, keep to 0, 1, 2, ... . -Try not to depend on a particular format for this field; it *is* though guaranteed that a `sort` will -produce lexicographical order, and that the order will be maintained after an arbitrary number of -reorderings, unlike with [naive solutions](http://programmers.stackexchange.com/questions/266451/maintain-ordered-collection-by-updating-as-few-order-fields-as-possible). - -Remember to declare on the server which collections you want to be reorderable from the client. -Otherwise, the library will error because the client would be able to modify numerical fields in -any collection, which represents a security risk. - - -## Passing options to the Sortable library - - {{#sortable items= option1=value1 option2=value2...}} - {{#sortable items= options=myOptions}} - -For available options, please refer to [the main README](../README.md#options). You can pass them directly -or under the `options` object. Direct options (`key=value`) override those in `options`. It is best -to pass presentation-related options directly, and functionality-related settings in an `options` -object, as this will enable designers to work without needing to inspect the JavaScript code: - - - -Define the options in a helper for the template that calls Sortable: - -```js -Template.myTemplate.helpers({ - playerOptions: function () { - return { - group: { - name: "league", - pull: true, - put: false - }, - sort: false - }; - } -}); -``` - -#### Meteor-specific options - -* `selector` - you can specify a collection selector if your list operates only on a subset of the collection. Example: - -```js -Template.myTemplate.helpers({ - playerOptions: function() { - return { - selector: { city: 'San Francisco' } - } - } -}); -``` - - -## Events - -All the original Sortable events are supported. In addition, they will receive -the data context in `event.data`. You can access `event.data.order` this way: - -```handlebars -{{#sortable items=players options=playersOptions}} -``` - -```js -Template.myTemplate.helpers({ - playersOptions: function () { - return { - onSort: function(/**Event*/event) { - console.log('Moved player #%d from %d to %d', - event.data.order, event.oldIndex, event.newIndex - ); - } - }; - } -}); -``` - - -# Issues - -If you encounter an issue while using this package, please CC @dandv when you file it in this repo. - - -# TODO - -* Array support -* Tests -* Misc. - see reactivize.js -* [GitHub issues](https://github.com/RubaXa/Sortable/labels/%E2%98%84%20meteor) diff --git a/meteor/example/.meteor/.finished-upgraders b/meteor/example/.meteor/.finished-upgraders deleted file mode 100644 index 8a76103..0000000 --- a/meteor/example/.meteor/.finished-upgraders +++ /dev/null @@ -1,8 +0,0 @@ -# This file contains information which helps Meteor properly upgrade your -# app when you run 'meteor update'. You should check it into version control -# with your project. - -notices-for-0.9.0 -notices-for-0.9.1 -0.9.4-platform-file -notices-for-facebook-graph-api-2 diff --git a/meteor/example/.meteor/.gitignore b/meteor/example/.meteor/.gitignore deleted file mode 100644 index 4083037..0000000 --- a/meteor/example/.meteor/.gitignore +++ /dev/null @@ -1 +0,0 @@ -local diff --git a/meteor/example/.meteor/.id b/meteor/example/.meteor/.id deleted file mode 100644 index b39baa1..0000000 --- a/meteor/example/.meteor/.id +++ /dev/null @@ -1,7 +0,0 @@ -# This file contains a token that is unique to your project. -# Check it into your repository along with the rest of this directory. -# It can be used for purposes such as: -# - ensuring you don't accidentally deploy one app on top of another -# - providing package authors with aggregated statistics - -ir0jg2douy3yo5mehw diff --git a/meteor/example/.meteor/packages b/meteor/example/.meteor/packages deleted file mode 100644 index 6f65fb7..0000000 --- a/meteor/example/.meteor/packages +++ /dev/null @@ -1,10 +0,0 @@ -# Meteor packages used by this project, one per line. -# -# 'meteor add' and 'meteor remove' will edit this file for you, -# but you can also edit it by hand. - -meteor-platform -autopublish -insecure -rubaxa:sortable -fezvrasta:bootstrap-material-design diff --git a/meteor/example/.meteor/platforms b/meteor/example/.meteor/platforms deleted file mode 100644 index 8a3a35f..0000000 --- a/meteor/example/.meteor/platforms +++ /dev/null @@ -1,2 +0,0 @@ -browser -server diff --git a/meteor/example/.meteor/release b/meteor/example/.meteor/release deleted file mode 100644 index 315c635..0000000 --- a/meteor/example/.meteor/release +++ /dev/null @@ -1 +0,0 @@ -METEOR@1.1.0.3 diff --git a/meteor/example/.meteor/versions b/meteor/example/.meteor/versions deleted file mode 100644 index a65be54..0000000 --- a/meteor/example/.meteor/versions +++ /dev/null @@ -1,53 +0,0 @@ -autopublish@1.0.3 -autoupdate@1.2.1 -base64@1.0.3 -binary-heap@1.0.3 -blaze@2.1.2 -blaze-tools@1.0.3 -boilerplate-generator@1.0.3 -callback-hook@1.0.3 -check@1.0.5 -dburles:mongo-collection-instances@0.3.4 -ddp@1.1.0 -deps@1.0.7 -ejson@1.0.6 -fastclick@1.0.3 -fezvrasta:bootstrap-material-design@0.3.0 -geojson-utils@1.0.3 -html-tools@1.0.4 -htmljs@1.0.4 -http@1.1.0 -id-map@1.0.3 -insecure@1.0.3 -jquery@1.11.3_2 -json@1.0.3 -lai:collection-extensions@0.1.4 -launch-screen@1.0.2 -livedata@1.0.13 -logging@1.0.7 -meteor@1.1.6 -meteor-platform@1.2.2 -minifiers@1.1.5 -minimongo@1.0.8 -mobile-status-bar@1.0.3 -mongo@1.1.0 -observe-sequence@1.0.6 -ordered-dict@1.0.3 -random@1.0.3 -reactive-dict@1.1.0 -reactive-var@1.0.5 -reload@1.1.3 -retry@1.0.3 -routepolicy@1.0.5 -rubaxa:sortable@1.2.1 -session@1.1.0 -spacebars@1.0.6 -spacebars-compiler@1.0.6 -templating@1.1.1 -tracker@1.0.7 -twbs:bootstrap@3.3.5 -ui@1.0.6 -underscore@1.0.3 -url@1.0.4 -webapp@1.2.0 -webapp-hashing@1.0.3 diff --git a/meteor/example/README.md b/meteor/example/README.md deleted file mode 100644 index 75c3d63..0000000 --- a/meteor/example/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# RubaXa:Sortable Meteor demo - -This demo showcases the two-way integration between the reorderable list -widget [Sortable](https://github.com/RubaXa/Sortable/) and Meteor.js. Meteor -Mongo collections are updated when items are added, removed or reordered, and -the order is persisted. - -It also shows list grouping and control over what lists can give or receive -elements. You can only drag elements from the list to the left onto the list -to the right. - - -## Usage - -The example uses the local package from the checkout, with the help of the run script: - -### Windows - - git clone https://github.com/RubaXa/Sortable.git - cd Sortable - # git checkout dev # optional - meteor\example\run.bat - -### Elsewhere - - git clone https://github.com/RubaXa/Sortable.git - cd Sortable - # git checkout dev # optional - meteor/example/run.sh - - -## [Prior art](http://slides.com/dandv/prior-art) - -### Differential - -Differential wrote [a blog post on reorderable lists with -Meteor](http://differential.com/blog/sortable-lists-in-meteor-using-jquery-ui) and -[jQuery UI Sortable](http://jqueryui.com/sortable/). It served as inspiration -for integrating [rubaxa:sortable](rubaxa.github.io/Sortable/), -which uses the HTML5 native drag&drop API (not without [its -limitations](https://github.com/RubaXa/Sortable/issues/106)). -The reordering method used by the Differential example can lead to data loss -though, because it calculates the new order of a dropped element as the -arithmetic mean of the elements before and after it. This [runs into limitations -of floating point precision](http://programmers.stackexchange.com/questions/266451/maintain-ordered-collection-by-updating-as-few-order-fields-as-possible) -in JavaScript after <50 reorderings. - -### Todos animated - -http://todos-dnd-animated.meteor.com/ ([source](https://github.com/nleush/meteor-todos-sortable-animation)) -is based on old Meteor Blaze (back then Spark) API, and won't work with current versions. -It does showcase some neat features, such as animation when collection elements -are reordered by another client. It uses jQuery UI Sortable as well, which lacks -some features vs. rubaxa:Sortable, e.g. text selection within the item. - -## TODO - -* Animation -* Indication that an item is being edited diff --git a/meteor/example/client/define-object-type.css b/meteor/example/client/define-object-type.css deleted file mode 100644 index d67b4b1..0000000 --- a/meteor/example/client/define-object-type.css +++ /dev/null @@ -1,57 +0,0 @@ -.glyphicon { - vertical-align: baseline; - font-size: 80%; - margin-right: 0.5em; -} - -[class^="mdi-"], [class*=" mdi-"] { - vertical-align: baseline; - font-size: 90%; - margin-right: 0.4em; -} - -.list-pair { - display: flex; /* use the flexbox model */ - flex-direction: row; -} -.sortable { -/* font-size: 2em;*/ -} - -.sortable.source { - /*background: #9FA8DA;*/ - flex: 0 0 auto; - margin-right: 1em; - cursor: move; - cursor: -webkit-grabbing; -} - -.sortable.target { - /*background: #3F51B5;*/ - flex: 1 1 auto; - margin-left: 1em; -} - -.target .well { - -} - -.sortable-handle { - cursor: move; - cursor: -webkit-grabbing; -} -.sortable-handle.pull-right { - margin-top: 0.3em; -} - -.sortable-ghost { - opacity: 0.6; -} - -/* show the remove button on hover */ -.removable .close { - display: none; -} -.removable:hover .close { - display: block; -} diff --git a/meteor/example/client/define-object-type.html b/meteor/example/client/define-object-type.html deleted file mode 100644 index d852cbc..0000000 --- a/meteor/example/client/define-object-type.html +++ /dev/null @@ -1,94 +0,0 @@ - - Reactive RubaXa:Sortable for Meteor - - - - {{> navbar}} - -
- - - {{> typeDefinition}} -
- - - - - - - - \ No newline at end of file diff --git a/meteor/example/client/define-object-type.js b/meteor/example/client/define-object-type.js deleted file mode 100644 index ad2a19e..0000000 --- a/meteor/example/client/define-object-type.js +++ /dev/null @@ -1,101 +0,0 @@ -// Define an object type by dragging together attributes - -Template.typeDefinition.helpers({ - types: function () { - return Types.find({}, { sort: { order: 1 } }); - }, - typesOptions: { - sortField: 'order', // defaults to 'order' anyway - group: { - name: 'typeDefinition', - pull: 'clone', - put: false - }, - sort: false // don't allow reordering the types, just the attributes below - }, - - attributes: function () { - return Attributes.find({}, { - sort: { order: 1 }, - transform: function (doc) { - doc.icon = Types.findOne({name: doc.type}).icon; - return doc; - } - }); - }, - attributesOptions: { - group: { - name: 'typeDefinition', - put: true - }, - onAdd: function (event) { - delete event.data._id; // Generate a new id when inserting in the Attributes collection. Otherwise, if we add the same type twice, we'll get an error that the ids are not unique. - delete event.data.icon; - event.data.type = event.data.name; - event.data.name = 'Rename me (double click)' - }, - // event handler for reordering attributes - onSort: function (event) { - console.log('Item %s went from #%d to #%d', - event.data.name, event.oldIndex, event.newIndex - ); - } - } -}); - -Template.sortableItemTarget.events({ - 'dblclick .name': function (event, template) { - // Make the name editable. We should use an existing component, but it's - // in a sorry state - https://github.com/arillo/meteor-x-editable/issues/1 - var name = template.$('.name'); - var input = template.$('input'); - if (input.length) { // jQuery never returns null - http://stackoverflow.com/questions/920236/how-can-i-detect-if-a-selector-returns-null - input.show(); - } else { - input = $(''); - name.after(input); - } - name.hide(); - input.focus(); - }, - 'blur input[type=text]': function (event, template) { - // commit the change to the name, if any - var input = template.$('input'); - input.hide(); - template.$('.name').show(); - // TODO - what is the collection here? We'll hard-code for now. - // https://github.com/meteor/meteor/issues/3303 - if (this.name !== input.val() && this.name !== '') - Attributes.update(this._id, {$set: {name: input.val()}}); - }, - 'keydown input[type=text]': function (event, template) { - if (event.which === 27) { - // ESC - discard edits and keep existing value - template.$('input').val(this.name); - event.preventDefault(); - event.target.blur(); - } else if (event.which === 13) { - // ENTER - event.preventDefault(); - event.target.blur(); - } - } -}); - -// you can add events to all Sortable template instances -Template.sortable.events({ - 'click .close': function (event, template) { - // `this` is the data context set by the enclosing block helper (#each, here) - template.collection.remove(this._id); - // custom code, working on a specific collection - if (Attributes.find().count() === 0) { - Meteor.setTimeout(function () { - Attributes.insert({ - name: 'Not nice to delete the entire list! Add some attributes instead.', - type: 'String', - order: 0 - }) - }, 1000); - } - } -}); diff --git a/meteor/example/model.js b/meteor/example/model.js deleted file mode 100644 index 8ae8822..0000000 --- a/meteor/example/model.js +++ /dev/null @@ -1,2 +0,0 @@ -Types = new Mongo.Collection('types'); -Attributes = new Mongo.Collection('attributes'); diff --git a/meteor/example/run.bat b/meteor/example/run.bat deleted file mode 100755 index b2a0d8b..0000000 --- a/meteor/example/run.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off -REM Sanity check: make sure we're in the directory of the script -set DIR=%~dp0 -cd %DIR% - -set PACKAGE_DIRS=..\..\ -meteor run %* diff --git a/meteor/example/run.sh b/meteor/example/run.sh deleted file mode 100755 index 8a9123a..0000000 --- a/meteor/example/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -# sanity check: make sure we're in the root directory of the example -cd "$( dirname "$0" )" - -# let Meteor find the local package -PACKAGE_DIRS=../../ meteor run "$@" diff --git a/meteor/example/server/fixtures.js b/meteor/example/server/fixtures.js deleted file mode 100644 index 617acf9..0000000 --- a/meteor/example/server/fixtures.js +++ /dev/null @@ -1,75 +0,0 @@ -Meteor.startup(function () { - if (Types.find().count() === 0) { - [ - { - name: 'String', - icon: '' - }, - { - name: 'Text, multi-line', - icon: '' - }, - { - name: 'Category', - icon: '' - }, - { - name: 'Number', - icon: '' - }, - { - name: 'Date', - icon: '' - }, - { - name: 'Hyperlink', - icon: '' - }, - { - name: 'Image', - icon: '' - }, - { - name: 'Progress', - icon: '' - }, - { - name: 'Duration', - icon: '' - }, - { - name: 'Map address', - icon: '' - }, - { - name: 'Relationship', - icon: '' - } - ].forEach(function (type, i) { - Types.insert({ - name: type.name, - icon: type.icon, - order: i - }); - } - ); - console.log('Initialized attribute types.'); - } - - if (Attributes.find().count() === 0) { - [ - { name: 'Name', type: 'String' }, - { name: 'Created at', type: 'Date' }, - { name: 'Link', type: 'Hyperlink' }, - { name: 'Owner', type: 'Relationship' } - ].forEach(function (attribute, i) { - Attributes.insert({ - name: attribute.name, - type: attribute.type, - order: i - }); - } - ); - console.log('Created sample object type.'); - } -}); diff --git a/meteor/example/server/sortable-collections.js b/meteor/example/server/sortable-collections.js deleted file mode 100644 index 76069a5..0000000 --- a/meteor/example/server/sortable-collections.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -Sortable.collections = ['attributes']; diff --git a/meteor/methods-client.js b/meteor/methods-client.js deleted file mode 100644 index 52f4ebe..0000000 --- a/meteor/methods-client.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -Meteor.methods({ - /** - * Update the sortField of documents with given ids in a collection, incrementing it by incDec - * @param {String} collectionName - name of the collection to update - * @param {String[]} ids - array of document ids - * @param {String} orderField - the name of the order field, usually "order" - * @param {Number} incDec - pass 1 or -1 - */ - 'rubaxa:sortable/collection-update': function (collectionName, ids, sortField, incDec) { - var selector = {_id: {$in: ids}}, modifier = {$inc: {}}; - modifier.$inc[sortField] = incDec; - Mongo.Collection.get(collectionName).update(selector, modifier, {multi: true}); - } -}); diff --git a/meteor/methods-server.js b/meteor/methods-server.js deleted file mode 100644 index 9598bfc..0000000 --- a/meteor/methods-server.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -Sortable = {}; -Sortable.collections = []; // array of collection names that the client is allowed to reorder - -Meteor.methods({ - /** - * Update the sortField of documents with given ids in a collection, incrementing it by incDec - * @param {String} collectionName - name of the collection to update - * @param {String[]} ids - array of document ids - * @param {String} orderField - the name of the order field, usually "order" - * @param {Number} incDec - pass 1 or -1 - */ - 'rubaxa:sortable/collection-update': function (collectionName, ids, sortField, incDec) { - check(collectionName, String); - // don't allow the client to modify just any collection - if (!Sortable || !Array.isArray(Sortable.collections)) { - throw new Meteor.Error(500, 'Please define Sortable.collections'); - } - if (Sortable.collections.indexOf(collectionName) === -1) { - throw new Meteor.Error(403, 'Collection <' + collectionName + '> is not Sortable. Please add it to Sortable.collections in server code.'); - } - - check(ids, [String]); - check(sortField, String); - check(incDec, Number); - var selector = {_id: {$in: ids}}, modifier = {$inc: {}}; - modifier.$inc[sortField] = incDec; - Mongo.Collection.get(collectionName).update(selector, modifier, {multi: true}); - } -}); diff --git a/meteor/package.js b/meteor/package.js deleted file mode 100644 index aaa048e..0000000 --- a/meteor/package.js +++ /dev/null @@ -1,85 +0,0 @@ -// Package metadata file for Meteor.js -'use strict'; - -var packageName = 'rubaxa:sortable'; // https://atmospherejs.com/rubaxa/sortable -var gitHubPath = 'RubaXa/Sortable'; // https://github.com/RubaXa/Sortable -var npmPackageName = 'sortablejs'; // https://www.npmjs.com/package/sortablejs - optional but recommended; used as fallback if GitHub fails - -/* All of the below is just to get the version number of the 3rd party library. - * First we'll try to read it from package.json. This works when publishing or testing the package - * but not when running an example app that uses a local copy of the package because the current - * directory will be that of the app, and it won't have package.json. Finding the path of a file is hard: - * http://stackoverflow.com/questions/27435797/how-do-i-obtain-the-path-of-a-file-in-a-meteor-package - * Therefore, we'll fall back to GitHub (which is more frequently updated), and then to NPMJS. - * We also don't have the HTTP package at this stage, and if we use Package.* in the request() callback, - * it will error that it must be run in a Fiber. So we'll use Node futures. - */ -var request = Npm.require('request'); -var Future = Npm.require('fibers/future'); - -var fut = new Future; -var version; - -if (!version) try { - var packageJson = JSON.parse(Npm.require('fs').readFileSync('../package.json')); - version = packageJson.version; -} catch (e) { - // if the file was not found, fall back to GitHub - console.warn('Could not find ../package.json to read version number from; trying GitHub...'); - var url = 'https://api.github.com/repos/' + gitHubPath + '/tags'; - request.get({ - url: url, - headers: { - 'User-Agent': 'request' // GitHub requires it - } - }, function (error, response, body) { - if (!error && response.statusCode === 200) { - var versions = JSON.parse(body).map(function (version) { - return version['name'].replace(/^\D+/, '') // trim leading non-digits from e.g. "v4.3.0" - }).sort(); - fut.return(versions[versions.length -1]); - } else { - // GitHub API rate limit reached? Fall back to npmjs. - console.warn('GitHub request to', url, 'failed:\n ', response && response.statusCode, response && response.body, error || '', '\nTrying NPMJS...'); - url = 'http://registry.npmjs.org/' + npmPackageName + '/latest'; - request.get(url, function (error, response, body) { - if (!error && response.statusCode === 200) - fut.return(JSON.parse(body).version); - else - fut.throw('Could not get version information from ' + url + ' either (incorrect package name?):\n' + (response && response.statusCode || '') + (response && response.body || '') + (error || '')); - }); - } - }); - - version = fut.wait(); -} - -// Now that we finally have an accurate version number... -Package.describe({ - name: packageName, - summary: 'Sortable: reactive minimalist reorderable drag-and-drop lists on modern browsers and touch devices', - version: version, - git: 'https://github.com/RubaXa/Sortable.git', - documentation: 'README.md' -}); - -Package.onUse(function (api) { - api.versionsFrom(['METEOR@0.9.0', 'METEOR@1.0']); - api.use('templating', 'client'); - api.use('dburles:mongo-collection-instances@0.3.4'); // to watch collections getting created - api.export('Sortable'); // exported on the server too, as a global to hold the array of sortable collections (for security) - api.addFiles([ - '../Sortable.js', - 'template.html', // the HTML comes first, so reactivize.js can refer to the template in it - 'reactivize.js' - ], 'client'); - api.addFiles('methods-client.js', 'client'); - api.addFiles('methods-server.js', 'server'); -}); - -Package.onTest(function (api) { - api.use(packageName, 'client'); - api.use('tinytest', 'client'); - - api.addFiles('test.js', 'client'); -}); diff --git a/meteor/publish.sh b/meteor/publish.sh deleted file mode 100755 index af392ca..0000000 --- a/meteor/publish.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# Publish package to Meteor's repository, Atmospherejs.com - -# Make sure Meteor is installed, per https://www.meteor.com/install. -# The curl'ed script is totally safe; takes 2 minutes to read its source and check. -type meteor >/dev/null 2>&1 || { curl https://install.meteor.com/ | sh; } - -# sanity check: make sure we're in the directory of the script -cd "$( dirname "$0" )" - -# publish package, creating it if it's the first time we're publishing -PACKAGE_NAME=$(grep -i name package.js | head -1 | cut -d "'" -f 2) - -echo "Publishing $PACKAGE_NAME..." - -# Attempt to re-publish the package - the most common operation once the initial release has -# been made. If the package name was changed (rare), you'll have to pass the --create flag. -meteor publish "$@"; EXIT_CODE=$? -if (( $EXIT_CODE == 0 )); then - echo "Thanks for releasing a new version. You can see it at" - echo "https://atmospherejs.com/${PACKAGE_NAME/://}" -else - echo "We have an error. Please post it at https://github.com/RubaXa/Sortable/issues" -fi - -exit $EXIT_CODE diff --git a/meteor/reactivize.js b/meteor/reactivize.js deleted file mode 100644 index ae7ff4b..0000000 --- a/meteor/reactivize.js +++ /dev/null @@ -1,201 +0,0 @@ -/* -Make a Sortable reactive by binding it to a Mongo.Collection. -Calls `rubaxa:sortable/collection-update` on the server to update the sortField of affected records. - -TODO: - * supply consecutive values if the `order` field doesn't have any - * .get(DOMElement) - return the Sortable object of a DOMElement - * create a new _id automatically onAdd if the event.from list had pull: 'clone' - * support arrays - * sparse arrays - * tests - * drop onto existing empty lists - * insert back into lists emptied by dropping - * performance on dragging into long list at the beginning - * handle failures on Collection operations, e.g. add callback to .insert - * when adding elements, update ranks just for the half closer to the start/end of the list - * revisit http://programmers.stackexchange.com/questions/266451/maintain-ordered-collection-by-updating-as-few-order-fields-as-possible - * reproduce the insidious bug where the list isn't always sorted (fiddle with dragging #1 over #2, then back, then #N before #1) - - */ - -'use strict'; - -Template.sortable.created = function () { - var templateInstance = this; - // `this` is a template instance that can store properties of our choice - http://docs.meteor.com/#/full/template_inst - if (templateInstance.setupDone) return; // paranoid: only run setup once - // this.data is the data context - http://docs.meteor.com/#/full/template_data - // normalize all options into templateInstance.options, and remove them from .data - templateInstance.options = templateInstance.data.options || {}; - Object.keys(templateInstance.data).forEach(function (key) { - if (key === 'options' || key === 'items') return; - templateInstance.options[key] = templateInstance.data[key]; - delete templateInstance.data[key]; - }); - templateInstance.options.sortField = templateInstance.options.sortField || 'order'; - // We can get the collection via the .collection property of the cursor, but changes made that way - // will NOT be sent to the server - https://github.com/meteor/meteor/issues/3271#issuecomment-66656257 - // Thus we need to use dburles:mongo-collection-instances to get a *real* collection - if (templateInstance.data.items && templateInstance.data.items.collection) { - // cursor passed via items=; its .collection works client-only and has a .name property - templateInstance.collectionName = templateInstance.data.items.collection.name; - templateInstance.collection = Mongo.Collection.get(templateInstance.collectionName); - } else if (templateInstance.data.items) { - // collection passed via items=; does NOT have a .name property, but _name - templateInstance.collection = templateInstance.data.items; - templateInstance.collectionName = templateInstance.collection._name; - } else if (templateInstance.data.collection) { - // cursor passed directly - templateInstance.collectionName = templateInstance.data.collection.name; - templateInstance.collection = Mongo.Collection.get(templateInstance.collectionName); - } else { - templateInstance.collection = templateInstance.data; // collection passed directly - templateInstance.collectionName = templateInstance.collection._name; - } - - // TODO if (Array.isArray(templateInstance.collection)) - - // What if user filters some of the items in the cursor, instead of ordering the entire collection? - // Use case: reorder by preference movies of a given genre, a filter within all movies. - // A: Modify all intervening items **that are on the client**, to preserve the overall order - // TODO: update *all* orders via a server method that takes not ids, but start & end elements - mild security risk - delete templateInstance.data.options; - - /** - * When an element was moved, adjust its orders and possibly the order of - * other elements, so as to maintain a consistent and correct order. - * - * There are three approaches to this: - * 1) Using arbitrary precision arithmetic and setting only the order of the moved - * element to the average of the orders of the elements around it - - * http://programmers.stackexchange.com/questions/266451/maintain-ordered-collection-by-updating-as-few-order-fields-as-possible - * The downside is that the order field in the DB will increase by one byte every - * time an element is reordered. - * 2) Adjust the orders of the intervening items. This keeps the orders sane (integers) - * but is slower because we have to modify multiple documents. - * TODO: we may be able to update fewer records by only altering the - * order of the records between the newIndex/oldIndex and the start/end of the list. - * 3) Use regular precision arithmetic, but when the difference between the orders of the - * moved item and the one before/after it falls below a certain threshold, adjust - * the order of that other item, and cascade doing so up or down the list. - * This will keep the `order` field constant in size, and will only occasionally - * require updating the `order` of other records. - * - * For now, we use approach #2. - * - * @param {String} itemId - the _id of the item that was moved - * @param {Number} orderPrevItem - the order of the item before it, or null - * @param {Number} orderNextItem - the order of the item after it, or null - */ - templateInstance.adjustOrders = function adjustOrders(itemId, orderPrevItem, orderNextItem) { - var orderField = templateInstance.options.sortField; - var selector = templateInstance.options.selector || {}, modifier = {$set: {}}; - var ids = []; - var startOrder = templateInstance.collection.findOne(itemId)[orderField]; - if (orderPrevItem !== null) { - // Element has a previous sibling, therefore it was moved down in the list. - // Decrease the order of intervening elements. - selector[orderField] = {$lte: orderPrevItem, $gt: startOrder}; - ids = _.pluck(templateInstance.collection.find(selector, {fields: {_id: 1}}).fetch(), '_id'); - Meteor.call('rubaxa:sortable/collection-update', templateInstance.collectionName, ids, orderField, -1); - - // Set the order of the dropped element to the order of its predecessor, whose order was decreased - modifier.$set[orderField] = orderPrevItem; - } else { - // element moved up the list, increase order of intervening elements - selector[orderField] = {$gte: orderNextItem, $lt: startOrder}; - ids = _.pluck(templateInstance.collection.find(selector, {fields: {_id: 1}}).fetch(), '_id'); - Meteor.call('rubaxa:sortable/collection-update', templateInstance.collectionName, ids, orderField, 1); - - // Set the order of the dropped element to the order of its successor, whose order was increased - modifier.$set[orderField] = orderNextItem; - } - templateInstance.collection.update(itemId, modifier); - }; - - templateInstance.setupDone = true; -}; - - -Template.sortable.rendered = function () { - var templateInstance = this; - var orderField = templateInstance.options.sortField; - - // sorting was changed within the list - var optionsOnUpdate = templateInstance.options.onUpdate; - templateInstance.options.onUpdate = function sortableUpdate(/**Event*/event) { - var itemEl = event.item; // dragged HTMLElement - event.data = Blaze.getData(itemEl); - if (event.newIndex < event.oldIndex) { - // Element moved up in the list. The dropped element has a next sibling for sure. - var orderNextItem = Blaze.getData(itemEl.nextElementSibling)[orderField]; - templateInstance.adjustOrders(event.data._id, null, orderNextItem); - } else if (event.newIndex > event.oldIndex) { - // Element moved down in the list. The dropped element has a previous sibling for sure. - var orderPrevItem = Blaze.getData(itemEl.previousElementSibling)[orderField]; - templateInstance.adjustOrders(event.data._id, orderPrevItem, null); - } else { - // do nothing - drag and drop in the same location - } - if (optionsOnUpdate) optionsOnUpdate(event); - }; - - // element was added from another list - var optionsOnAdd = templateInstance.options.onAdd; - templateInstance.options.onAdd = function sortableAdd(/**Event*/event) { - var itemEl = event.item; // dragged HTMLElement - event.data = Blaze.getData(itemEl); - // let the user decorate the object with additional properties before insertion - if (optionsOnAdd) optionsOnAdd(event); - - // Insert the new element at the end of the list and move it where it was dropped. - // We could insert it at the beginning, but that would lead to negative orders. - var sortSpecifier = {}; sortSpecifier[orderField] = -1; - event.data.order = templateInstance.collection.findOne({}, { sort: sortSpecifier, limit: 1 }).order + 1; - // TODO: this can obviously be optimized by setting the order directly as the arithmetic average, with the caveats described above - var newElementId = templateInstance.collection.insert(event.data); - event.data._id = newElementId; - if (itemEl.nextElementSibling) { - var orderNextItem = Blaze.getData(itemEl.nextElementSibling)[orderField]; - templateInstance.adjustOrders(newElementId, null, orderNextItem); - } else { - // do nothing - inserted after the last element - } - // remove the dropped HTMLElement from the list because we have inserted it in the collection, which will update the template - itemEl.parentElement.removeChild(itemEl); - }; - - // element was removed by dragging into another list - var optionsOnRemove = templateInstance.options.onRemove; - templateInstance.options.onRemove = function sortableRemove(/**Event*/event) { - var itemEl = event.item; // dragged HTMLElement - event.data = Blaze.getData(itemEl); - // don't remove from the collection if group.pull is clone or false - if (typeof templateInstance.options.group === 'undefined' - || typeof templateInstance.options.group.pull === 'undefined' - || templateInstance.options.group.pull === true - ) templateInstance.collection.remove(event.data._id); - if (optionsOnRemove) optionsOnRemove(event); - }; - - // just compute the `data` context - ['onStart', 'onEnd', 'onSort', 'onFilter'].forEach(function (eventHandler) { - if (templateInstance.options[eventHandler]) { - var userEventHandler = templateInstance.options[eventHandler]; - templateInstance.options[eventHandler] = function (/**Event*/event) { - var itemEl = event.item; // dragged HTMLElement - event.data = Blaze.getData(itemEl); - userEventHandler(event); - }; - } - }); - - templateInstance.sortable = Sortable.create(templateInstance.firstNode.parentElement, templateInstance.options); - // TODO make the object accessible, e.g. via Sortable.getSortableById() or some such -}; - - -Template.sortable.destroyed = function () { - if(this.sortable) this.sortable.destroy(); -}; diff --git a/meteor/runtests.bat b/meteor/runtests.bat deleted file mode 100644 index 9bddf93..0000000 --- a/meteor/runtests.bat +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -REM Test Meteor package before publishing to Atmospherejs.com - -REM Sanity check: make sure we're in the directory of the script -set DIR=%~dp0 -cd %DIR% - -meteor test-packages ./ %* diff --git a/meteor/runtests.sh b/meteor/runtests.sh deleted file mode 100755 index 0e738a7..0000000 --- a/meteor/runtests.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -# Test Meteor package before publishing to Atmospherejs.com - -# Make sure Meteor is installed, per https://www.meteor.com/install. -# The curl'ed script is totally safe; takes 2 minutes to read its source and check. -type meteor >/dev/null 2>&1 || { curl https://install.meteor.com/ | sh; } - -# sanity check: make sure we're in the directory of the script -cd "$( dirname "$0" )" - - -# delete the temporary files even if Ctrl+C is pressed -int_trap() { - printf "\nTests interrupted. Cleaning up...\n\n" -} -trap int_trap INT - - -EXIT_CODE=0 - -PACKAGE_NAME=$(grep -i name package.js | head -1 | cut -d "'" -f 2) - -echo "### Testing $PACKAGE_NAME..." - -# provide an invalid MONGO_URL so Meteor doesn't bog us down with an empty Mongo database -if [ $# -gt 0 ]; then - # interpret any parameter to mean we want an interactive test - MONGO_URL=mongodb:// meteor test-packages ./ -else - # automated/CI test with phantomjs - ./node_modules/.bin/spacejam --mongo-url mongodb:// test-packages ./ - EXIT_CODE=$(( $EXIT_CODE + $? )) -fi - -exit $EXIT_CODE diff --git a/meteor/template.html b/meteor/template.html deleted file mode 100644 index 3923d3d..0000000 --- a/meteor/template.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/meteor/test.js b/meteor/test.js deleted file mode 100644 index f7c00a9..0000000 --- a/meteor/test.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -Tinytest.add('Sortable.is', function (test) { - var items = document.createElement('ul'); - items.innerHTML = '
  • item 1
  • item 2
  • item 3
  • '; - var sortable = new Sortable(items); - test.instanceOf(sortable, Sortable, 'Instantiation OK'); - test.length(sortable.toArray(), 3, 'Three elements'); -});