condo_interface 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.textile CHANGED
@@ -1,5 +1,11 @@
1
- h1. condo_interface
1
+ h1. Condo Interface
2
2
 
3
3
  The default UI for the Condo project. Feel free to use, extend, adapt or write your own.
4
+ Condo Interface is under the terms of the MIT License.
4
5
 
5
6
 
7
+ h2. Usage
8
+
9
+ "Condo":https://github.com/cotag/Condominios is built as an "AngularJS application":http://angularjs.org/ which implements "models":http://docs.angularjs.org/guide/concepts#model and a "controller":http://docs.angularjs.org/guide/concepts#controller for a "Rails backend":http://rubyonrails.org/
10
+ Condo Interface is an example "view":http://docs.angularjs.org/guide/concepts#view with an associated "directive":http://docs.angularjs.org/guide/concepts#directives that interface with the Condo controller.
11
+
@@ -20,10 +20,10 @@
20
20
  (function (factory) {
21
21
  if (typeof define === 'function' && define.amd) {
22
22
  // AMD
23
- define(['jquery', 'condo_uploader'], factory);
23
+ define(['jquery', 'condo_controller'], factory);
24
24
  } else {
25
25
  // Browser globals
26
- factory(jQuery, window.CondoUploader);
26
+ factory(jQuery, window.CondoController);
27
27
  }
28
28
  }(function ($, uploads, undefined) {
29
29
  'use strict';
@@ -43,7 +43,10 @@
43
43
  // Allow for both mobile and desktop events or both
44
44
  // Overkill?
45
45
  //
46
- uploads.directive('coTap', function() {
46
+ var condoInterface = angular.module('CondoInterface', ['CondoUploader'])
47
+
48
+
49
+ condoInterface.directive('coTap', function() {
47
50
 
48
51
 
49
52
  //
@@ -145,94 +148,238 @@
145
148
  //
146
149
  // create a directive for attaching the input events
147
150
  //
148
- uploads.directive('coUploads', ['Condo.Broadcast', function(broadcast) {
149
- return function(scope, element, attrs) {
150
- var options = {
151
- delegate: attrs['coDelegate'] || element,
152
- drop_targets: attrs['coTargets'] || element,
153
- hover_class: attrs['coHoverClass'] || 'drag-hover',
154
- pre_check: attrs['coAccepts'] || '/./i',
155
- size_limit: attrs['coLimit'] || 0
156
- };
157
-
158
-
159
- if(!!attrs['coEndpoint'])
160
- scope.endpoint = attrs['coEndpoint'];
151
+ condoInterface.directive('coUploads', ['Condo.Broadcast', '$timeout', function(broadcast, $timeout) {
152
+ return {
153
+ controller: 'Condo.Controller',
154
+ link: function(scope, element, attrs) {
155
+ var options = {
156
+ delegate: attrs['coDelegate'] || element,
157
+ drop_targets: attrs['coTargets'] || element,
158
+ hover_class: attrs['coHoverClass'] || 'drag-hover',
159
+ pre_check: attrs['coAccepts'] || '/./i',
160
+ size_limit: attrs['coLimit'] || 0
161
+ },
162
+
163
+ //
164
+ // Add files with their path information to the system
165
+ // Queue items here until we decide they should be added to the view
166
+ //
167
+ processPending = function() {
168
+ var avaliable = view_limit - scope.upload_count;
169
+
170
+ if(avaliable > 0 && pending_items.length > 0) {
171
+
172
+ var item = pending_items.shift(),
173
+ items = item.items,
174
+ length = items.length;
175
+
176
+ if(item.folders) {
177
+ var i = 0,
178
+ entry,
179
+ obj,
180
+ count = 0,
181
+ new_items = [],
182
+ processEntry = function(entry, path) {
183
+ //
184
+ // If it is a directory we add it to the pending queue
185
+ //
186
+ try {
187
+ if (entry.isDirectory) {
188
+ entry.createReader().readEntries(function(entries) {
189
+
190
+ pending_items.push({
191
+ items: entries,
192
+ folders: true,
193
+ path: path + entry.name + '/'
194
+ });
195
+ checkCount();
196
+ });
197
+ } else if (entry.isFile) { // Files are added to a file queue
198
+ entry.file(function(file) {
199
+ if(path.length > 0)
200
+ file.dir_path = path;
201
+
202
+ new_items.push(file);
203
+
204
+ checkCount();
205
+ });
206
+ } else {
207
+ checkCount();
208
+ }
209
+ } catch(err) {
210
+ //
211
+ // TODO:: hmmmm
212
+ //
213
+ checkCount();
214
+ }
215
+
216
+ },
217
+ checkCount = function() {
218
+ //
219
+ // Counts the entries processed so we can add any files to the queue
220
+ //
221
+ count += 1;
222
+ if (count >= length) {
223
+ if(new_items.length > 0) {
224
+ pending_items.unshift({ // add any files to the start of the queue
225
+ items: new_items,
226
+ folders: false
227
+ });
228
+ }
229
+ safeApply(scope, function() {
230
+ $timeout(processPending);
231
+ });
232
+ }
233
+ };
234
+
235
+ for (; i < length; i++) {
236
+
237
+ //
238
+ // first layer of DnD folders require you to getAsEntry
239
+ //
240
+ if(item.path.length == 0) {
241
+ obj = items[i];
242
+ obj.getAsEntry = obj.getAsEntry || obj.webkitGetAsEntry || obj.mozGetAsEntry;
243
+ entry = obj.getAsEntry();
244
+ } else {
245
+ entry = items[i];
246
+ }
247
+ processEntry(entry, item.path);
248
+ }
249
+ } else if(length <= avaliable) { // Regular files where we can add them all at once
250
+ scope.add(items);
251
+ $timeout(processPending); // Delay until next tick (delay and invoke apply are optional)
252
+ } else { // Regular file where we can't add them all at once
253
+ scope.add(items.splice(0, avaliable));
254
+ pending_items.unshift(item);
255
+ }
256
+ }
257
+ },
258
+ view_limit = 50, // Number of uploads that should be displayed at once
259
+ pending_items = []; // These are files or folders that have not been processed yet as we are at the view port limit
161
260
 
162
261
 
163
- scope.options = options;
164
-
165
-
166
- //
167
- // Determine how to draw the element
168
- //
169
- if(document.implementation.hasFeature("org.w3c.svg", "1.0")) {
170
- element.addClass('supports-svg');
171
- } else {
172
- element.addClass('no-svg');
173
- }
262
+ if(!!attrs['coEndpoint'])
263
+ scope.endpoint = attrs['coEndpoint'];
264
+
174
265
 
266
+ scope.options = options;
267
+ scope.remove_completed = false; // Remove completed uploads automatically
175
268
 
176
- //
177
- // Detect file drops
178
- //
179
- options.drop_targets = $(options.drop_targets);
180
- options.delegate = $(options.delegate).on('drop.condo', options.drop_targets, function(event) {
181
- options.drop_targets.removeClass(options.hover_class);
182
269
 
183
270
  //
184
- // Prevent propagation early (so any errors don't cause unwanted behaviour)
271
+ // Determine how to draw the element
185
272
  //
186
- event.preventDefault();
187
- event.stopPropagation();
273
+ if(document.implementation.hasFeature("org.w3c.svg", "1.0")) {
274
+ element.addClass('supports-svg');
275
+ } else {
276
+ element.addClass('no-svg');
277
+ }
278
+
279
+
280
+ //
281
+ // Detect file drops
282
+ //
283
+ options.drop_targets = $(options.drop_targets);
284
+ options.delegate = $(options.delegate).on('drop.condo', options.drop_targets, function(event) {
285
+ options.drop_targets.removeClass(options.hover_class);
286
+
287
+ //
288
+ // Prevent propagation early (so any errors don't cause unwanted behaviour)
289
+ //
290
+ event.preventDefault();
291
+ event.stopPropagation();
292
+
293
+
294
+ if (!!event.originalEvent.dataTransfer.items) {
295
+ pending_items.push({
296
+ items: event.originalEvent.dataTransfer.items,
297
+ folders: true,
298
+ path: ''
299
+ });
300
+ } else if(!!event.originalEvent.dataTransfer.files) {
301
+ var files = event.originalEvent.dataTransfer.files,
302
+ copy = [],
303
+ i = 0;
304
+
305
+ for (; i < files.length; i += 1)
306
+ copy.push(files[i]);
307
+
308
+ pending_items.push({
309
+ items: copy,
310
+ folders: false
311
+ });
312
+ }
313
+
314
+ safeApply(scope, function() {
315
+ processPending();
316
+ });
317
+ }).on('dragover.condo', options.drop_targets, function(event) {
318
+ $(this).addClass(options.hover_class);
319
+
320
+ return false;
321
+ }).on('dragleave.condo', options.drop_targets, function(event) {
322
+ $(this).removeClass(options.hover_class);
323
+
324
+ return false;
325
+ }).
326
+
188
327
 
189
- safeApply(scope, function() {
190
- scope.add(event.originalEvent.dataTransfer.files);
328
+ //
329
+ // Detect manual file uploads
330
+ //
331
+ on('change.condo', ':file', function(event) {
332
+ var files = $(this)[0].files,
333
+ copy = [],
334
+ i = 0;
335
+
336
+ for (; i < files.length; i += 1)
337
+ copy.push(files[i]);
338
+
339
+ $(this).parents('form')[0].reset();
340
+
341
+ pending_items.push({
342
+ items: copy,
343
+ folders: false
344
+ });
345
+
346
+ safeApply(scope, function() {
347
+ processPending();
348
+ });
191
349
  });
192
- }).on('dragover.condo', options.drop_targets, function(event) {
193
- $(this).addClass(options.hover_class);
194
350
 
195
- return false;
196
- }).on('dragleave.condo', options.drop_targets, function(event) {
197
- $(this).removeClass(options.hover_class);
198
351
 
199
- return false;
200
- }).
201
-
202
-
203
- //
204
- // Detect manual file uploads
205
- //
206
- on('change.condo', ':file', function(event) {
207
- var self = $(this);
208
- safeApply(scope, function() {
209
- scope.add(self[0].files);
210
- self.parents('form')[0].reset();
352
+ //
353
+ // Add new uploads if possible
354
+ //
355
+ scope.$watch('upload_count', function(newValue, oldValue) {
356
+ processPending();
211
357
  });
212
- });
213
-
214
-
215
- //
216
- // Clean up any event handlers
217
- //
218
- scope.$on('$destroy', function() {
219
- options.drop_targets.off('.condo');
220
- options.delegate.off('.condo');
221
- element.removeClass('supports-svg').removeClass('no-svg');
222
- });
223
-
224
-
225
- scope.$on('coFileAddFailed', function() {
226
- alert('Failed to add file: ' + broadcast.message.reason);
227
- });
228
-
229
-
230
- scope.humanReadableByteCount = function(bytes, si) {
231
- var unit = si ? 1000.0 : 1024.0;
232
- if (bytes < unit) return bytes + (si ? ' iB' : ' B');
233
- var exp = Math.floor(Math.log(bytes) / Math.log(unit)),
234
- pre = (si ? 'kMGTPE' : 'KMGTPE').charAt(exp-1) + (si ? 'iB' : 'B');
235
- return (bytes / Math.pow(unit, exp)).toFixed(1) + ' ' + pre;
358
+
359
+
360
+ //
361
+ // Clean up any event handlers
362
+ //
363
+ scope.$on('$destroy', function() {
364
+ options.drop_targets.off('.condo');
365
+ options.delegate.off('.condo');
366
+ element.removeClass('supports-svg').removeClass('no-svg');
367
+ });
368
+
369
+
370
+ scope.$on('coFileAddFailed', function() {
371
+ // TODO:: need an unobtrusive notification system for failed adds
372
+ // alert('Failed to add file: ' + broadcast.message.reason);
373
+ });
374
+
375
+
376
+ scope.humanReadableByteCount = function(bytes, si) {
377
+ var unit = si ? 1000.0 : 1024.0;
378
+ if (bytes < unit) return bytes + (si ? ' iB' : ' B');
379
+ var exp = Math.floor(Math.log(bytes) / Math.log(unit)),
380
+ pre = (si ? 'kMGTPE' : 'KMGTPE').charAt(exp-1) + (si ? 'iB' : 'B');
381
+ return (bytes / Math.pow(unit, exp)).toFixed(1) + ' ' + pre;
382
+ }
236
383
  }
237
384
  }
238
385
  }]);
@@ -242,7 +389,7 @@
242
389
  // The individual upload events
243
390
  // Triggers the pause, resume, abort functions
244
391
  //
245
- uploads.directive('coUpload', function() {
392
+ condoInterface.directive('coUpload', function() {
246
393
  var PENDING = 0,
247
394
  STARTED = 1,
248
395
  PAUSED = 2,
@@ -274,7 +421,10 @@
274
421
  element.find('td.controls').replaceWith( '<td class="blank" />' );
275
422
  element.find('div.bar').removeClass('animate');
276
423
 
277
- scope.check_autostart();
424
+ if(scope.remove_completed)
425
+ scope.animate_remove();
426
+ else
427
+ scope.check_autostart(); // Couldn't work out how to put this into the controller
278
428
  break;
279
429
 
280
430
  case PAUSED:
@@ -284,6 +434,9 @@
284
434
 
285
435
  scope.paused = true;
286
436
  // No need for break
437
+
438
+ if (scope.ignore_errors && scope.upload.error)
439
+ scope.check_autostart(); // Couldn't work out how to put this into the controller
287
440
  }
288
441
  });
289
442
 
@@ -305,4 +458,62 @@
305
458
  };
306
459
  });
307
460
 
461
+
462
+ //
463
+ // Toggling options
464
+ // based on: https://github.com/angular-ui/bootstrap/tree/master/src/dropdownToggle
465
+ //
466
+ condoInterface.directive('dropdownToggle', ['$document', '$location', '$window', function ($document, $location, $window) {
467
+ var openElement = null, close;
468
+ return {
469
+ restrict: 'CA',
470
+ link: function(scope, element, attrs) {
471
+ scope.$watch(function dropdownTogglePathWatch() {return $location.path();}, function dropdownTogglePathWatchAction() {
472
+ if (close) { close(); }
473
+ });
474
+
475
+ element.parent().bind('click', function(event) {
476
+ event.stopPropagation();
477
+ });
478
+
479
+ element.bind('click', function(event) {
480
+ event.preventDefault();
481
+ event.stopPropagation();
482
+
483
+ var iWasOpen = false;
484
+
485
+ if (openElement) {
486
+ iWasOpen = openElement === element;
487
+ close();
488
+ }
489
+
490
+ if (!iWasOpen){
491
+ element.parent().addClass('open');
492
+ openElement = element;
493
+
494
+ close = function (event) {
495
+ if (event) {
496
+ event.preventDefault();
497
+ event.stopPropagation();
498
+ }
499
+ $document.unbind('click', close);
500
+ element.parent().removeClass('open');
501
+ close = null;
502
+ openElement = null;
503
+ };
504
+
505
+ $document.bind('click', close);
506
+ }
507
+ });
508
+
509
+
510
+ //
511
+ // Center the pop-up, based on CSS location of the button
512
+ //
513
+ var popup = element.next('ul.dropdown-menu');
514
+ popup.css('margin-left', -(popup.width() / 2) + 'px');
515
+ }
516
+ };
517
+ }]);
518
+
308
519
  }));
@@ -9,6 +9,7 @@
9
9
  * compiled file, but it's generally better to create a new file per style scope.
10
10
  *
11
11
  *= require_self
12
+ *= require condo_interface/bootstrap/condo_custom
12
13
  *= require condo_interface/uploads
13
14
  *= require condo_interface/images
14
15
  *= require_tree .