Permalink
Browse files

Backbone.js Tutorial: jQuery Plugins

  • Loading branch information...
1 parent 705bcb4 commit e9edfa3157fd460a9c82faa6c6bb9d87ccdca443 Alex Young committed Apr 4, 2013
View
@@ -35,6 +35,8 @@ h1 { margin: 0 0 10px 0; padding: 0; text-align: center }
.completed { text-decoration: line-through; }
+li.sortable-placeholder { border: 1px dashed #CCC; background: none; height: 28px; margin: 5px 0 }
+
@media (max-width: 570px) {
.hide-tiny { display: none }
.row-fluid .span6 { width: 100%; margin-left: 0 }
@@ -19,6 +19,19 @@ define(['models/task'], function(Task) {
request = gapi.client.tasks.tasks.clear({ tasklist: tasklist });
Backbone.gapiRequest(request, 'update', this, options);
+ },
+
+ move: function(id, previousId, list) {
+ var model = this.get(id)
+ , toModel = this.get(previousId)
+ , index = this.indexOf(toModel) + 1
+ ;
+
+ this.remove(model, { silent: true });
+ this.add(model, { at: index, silent: true });
+
+ // Persist the change
+ list.moveTask({ task: id, previous: previousId });
}
});
@@ -0,0 +1,85 @@
+/*
+ * HTML5 Sortable jQuery Plugin
+ * http://farhadi.ir/projects/html5sortable
+ *
+ * Copyright 2012, Ali Farhadi
+ * Released under the MIT license.
+ */
+(function($) {
+var dragging, placeholders = $();
+$.fn.sortable = function(options) {
+ var method = String(options);
+ options = $.extend({
+ connectWith: false
+ }, options);
+ return this.each(function() {
+ if (/^enable|disable|destroy$/.test(method)) {
+ var items = $(this).children($(this).data('items')).attr('draggable', method == 'enable');
+ if (method == 'destroy') {
+ items.add(this).removeData('connectWith items')
+ .off('dragstart.h5s dragend.h5s selectstart.h5s dragover.h5s dragenter.h5s drop.h5s');
+ }
+ return;
+ }
+ var isHandle, index, items = $(this).children(options.items);
+ var placeholder = $('<' + (/^ul|ol$/i.test(this.tagName) ? 'li' : 'div') + ' class="sortable-placeholder">');
+ items.find(options.handle).mousedown(function() {
+ isHandle = true;
+ }).mouseup(function() {
+ isHandle = false;
+ });
+ $(this).data('items', options.items)
+ placeholders = placeholders.add(placeholder);
+ if (options.connectWith) {
+ $(options.connectWith).add(this).data('connectWith', options.connectWith);
+ }
+ items.attr('draggable', 'true').on('dragstart.h5s', function(e) {
+ if (options.handle && !isHandle) {
+ return false;
+ }
+ isHandle = false;
+ var dt = e.originalEvent.dataTransfer;
+ dt.effectAllowed = 'move';
+ dt.setData('Text', 'dummy');
+ index = (dragging = $(this)).addClass('sortable-dragging').index();
+ }).on('dragend.h5s', function() {
+ if (!dragging) {
+ return;
+ }
+ dragging.removeClass('sortable-dragging').show();
+ placeholders.detach();
+ if (index != dragging.index()) {
+ dragging.parent().trigger('sortupdate', {item: dragging});
+ }
+ dragging = null;
+ }).not('a[href], img').on('selectstart.h5s', function() {
+ this.dragDrop && this.dragDrop();
+ return false;
+ }).end().add([this, placeholder]).on('dragover.h5s dragenter.h5s drop.h5s', function(e) {
+ if (!items.is(dragging) && options.connectWith !== $(dragging).parent().data('connectWith')) {
+ return true;
+ }
+ if (e.type == 'drop') {
+ e.stopPropagation();
+ placeholders.filter(':visible').after(dragging);
+ dragging.trigger('dragend.h5s');
+ return false;
+ }
+ e.preventDefault();
+ e.originalEvent.dataTransfer.dropEffect = 'move';
+ if (items.is(this)) {
+ if (options.forcePlaceholderSize) {
+ placeholder.height(dragging.outerHeight());
+ }
+ dragging.hide();
+ $(this)[placeholder.index() < $(this).index() ? 'after' : 'before'](placeholder);
+ placeholders.not(placeholder).detach();
+ } else if (!placeholders.is(this) && !$(this).children(options.items).length) {
+ placeholders.detach();
+ $(this).append(placeholder);
+ }
+ return false;
+ });
+ });
+};
+})(jQuery);
View
@@ -14,7 +14,7 @@ requirejs.config({
, exports: 'Backbone'
},
'app': {
- deps: ['lib/underscore-min', 'lib/backbone']
+ deps: ['lib/underscore-min', 'lib/backbone', 'lib/jquery.sortable']
}
}
});
@@ -1,6 +1,13 @@
define(function() {
var TaskList = Backbone.Model.extend({
- url: 'tasklists'
+ url: 'tasklists',
+
+ moveTask: function(options) {
+ options['tasklist'] = this.get('id');
+ var request = gapi.client.tasks.tasks.move(options);
+
+ Backbone.gapiRequest(request, 'update', this, options);
+ }
});
return TaskList;
@@ -1,3 +1,4 @@
<input type="checkbox" data-task-id="{{id}}" name="task_check_{{id}}" class="check-task" value="t">
<span class="title {{status}}">{{title}}</span>
<span class="notes">{{notes}}</span>
+<a href="#" class="handle pull-right"><i class="icon-move"></i></a>
@@ -14,6 +14,24 @@ define(['text!templates/tasks/index.html', 'views/tasks/task', 'views/tasks/edit
this.collection.on('add', this.renderTask, this);
},
+ makeSortable: function() {
+ var $el = this.$el.find('#task-list');
+ if (this.collection.length) {
+ $el.sortable('destroy');
+ $el.sortable({ handle: '.handle' }).bind('sortupdate', _.bind(this.saveTaskOrder, this));
+ }
+ },
+
+ saveTaskOrder: function(e, o) {
+ var id = $(o.item).find('.check-task').data('taskId')
+ , previous = $(o.item).prev()
+ , previousId = previous.length ? $(previous).find('.check-task').data('taskId') : null
+ , request
+ ;
+
+ this.collection.move(id, previousId, this.model);
+ },
+
addTask: function() {
var $input = this.$el.find('input[name="title"]')
, task = new this.collection.model({ tasklist: this.model.get('id') })
@@ -57,6 +75,8 @@ define(['text!templates/tasks/index.html', 'views/tasks/task', 'views/tasks/edit
task.set('tasklist', self.model.get('id'));
self.renderTask(task);
});
+
+ self.makeSortable();
}});
return this;

0 comments on commit e9edfa3

Please sign in to comment.