~ubuntu-branches/ubuntu/wily/phabricator/wily

1 by Richard Sellam
Import upstream version 0~git20141023
1
/**
2
 * @provides javelin-behavior-conpherence-menu
3
 * @requires javelin-behavior
4
 *           javelin-dom
5
 *           javelin-util
6
 *           javelin-stratcom
7
 *           javelin-workflow
8
 *           javelin-behavior-device
9
 *           javelin-history
10
 *           javelin-vector
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
11
 *           javelin-scrollbar
12
 *           phabricator-title
1 by Richard Sellam
Import upstream version 0~git20141023
13
 *           phabricator-shaped-request
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
14
 *           conpherence-thread-manager
1 by Richard Sellam
Import upstream version 0~git20141023
15
 */
16
17
JX.behavior('conpherence-menu', function(config) {
18
  /**
19
   * State for displayed thread.
20
   */
21
  var _thread = {
22
    selected: null,
23
    visible: null,
24
    node: null
25
  };
26
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
27
  var scrollbar = null;
28
29
  // TODO - move more logic into the ThreadManager
30
  var threadManager = new JX.ConpherenceThreadManager();
0.15.3 by Richard Sellam
Import upstream version 0~git20150525
31
  threadManager.setMessagesRootCallback(function() {
32
    return scrollbar.getContentNode();
33
  });
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
34
  threadManager.setWillLoadThreadCallback(function() {
35
    markThreadLoading(true);
36
  });
37
  threadManager.setDidLoadThreadCallback(function(r) {
38
    var header = JX.$H(r.header);
0.15.3 by Richard Sellam
Import upstream version 0~git20150525
39
    var messages = JX.$H(r.transactions);
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
40
    var form = JX.$H(r.form);
41
    var root = JX.DOM.find(document, 'div', 'conpherence-layout');
42
    var header_root = JX.DOM.find(root, 'div', 'conpherence-header-pane');
43
    var form_root = JX.DOM.find(root, 'div', 'conpherence-form');
44
    JX.DOM.setContent(header_root, header);
45
    JX.DOM.setContent(scrollbar.getContentNode(), messages);
46
    JX.DOM.setContent(form_root, form);
47
48
    markThreadLoading(false);
49
50
    didRedrawThread(true);
51
  });
52
53
  threadManager.setDidUpdateThreadCallback(function(r) {
54
    _scrollMessageWindow();
55
  });
56
57
  threadManager.setWillSendMessageCallback(function () {
58
    var root = JX.DOM.find(document, 'div', 'conpherence-layout');
59
    var form_root = JX.DOM.find(root, 'div', 'conpherence-form');
60
    markThreadLoading(true);
61
    JX.DOM.alterClass(form_root, 'loading', true);
62
  });
63
64
  threadManager.setDidSendMessageCallback(function (r, non_update) {
65
    var root = JX.DOM.find(document, 'div', 'conpherence-layout');
66
    var form_root = JX.DOM.find(root, 'div', 'conpherence-form');
67
    var textarea = JX.DOM.find(form_root, 'textarea');
68
    if (!non_update) {
69
      var fileWidget = null;
70
      try {
71
        fileWidget = JX.DOM.find(root, 'div', 'widgets-files');
72
      } catch (ex) {
73
        // Ignore; maybe no files widget
74
      }
75
      if (fileWidget) {
76
        JX.DOM.setContent(
77
          fileWidget,
78
          JX.$H(r.file_widget));
79
      }
0.15.3 by Richard Sellam
Import upstream version 0~git20150525
80
81
      _scrollMessageWindow();
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
82
      textarea.value = '';
83
    }
84
    markThreadLoading(false);
85
86
    setTimeout(function() { JX.DOM.focus(textarea); }, 100);
87
  });
88
  threadManager.start();
89
1 by Richard Sellam
Import upstream version 0~git20141023
90
  /**
91
   * Current role of this behavior. The two possible roles are to show a 'list'
92
   * of threads or a specific 'thread'. On devices, this behavior stays in the
93
   * 'list' role indefinitely, treating clicks normally and the next page
94
   * loads the behavior with role = 'thread'. On desktop, this behavior
95
   * auto-loads a thread as part of the 'list' role. As the thread loads the
96
   * role is changed to 'thread'.
97
   */
98
  var _currentRole = null;
99
100
  /**
101
   * When _oldDevice is null the code is executing for the first time.
102
   */
103
  var _oldDevice = null;
104
105
  /**
106
   * Initializes this behavior based on all the configuraton jonx and the
107
   * result of JX.Device.getDevice();
108
   */
109
  function init() {
110
    _currentRole = config.role;
111
112
    if (_currentRole == 'thread') {
113
      markThreadsLoading(true);
114
    } else {
115
      markThreadLoading(true);
116
    }
117
    markWidgetLoading(true);
118
    onDeviceChange();
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
119
    var root = JX.DOM.find(document, 'div', 'conpherence-layout');
120
    var messages_root = JX.DOM.find(root, 'div', 'conpherence-message-pane');
121
    var messages = JX.DOM.find(messages_root, 'div', 'conpherence-messages');
122
    scrollbar = new JX.Scrollbar(messages);
123
    scrollbar.setAsScrollFrame();
1 by Richard Sellam
Import upstream version 0~git20141023
124
  }
125
  init();
126
127
  /**
128
   * Selecting threads
129
   */
130
  function selectThreadByID(id, update_page_data) {
131
    var thread = JX.$(id);
132
    selectThread(thread, update_page_data);
133
  }
134
135
  function selectThread(node, update_page_data) {
136
    if (_thread.node) {
137
      JX.DOM.alterClass(_thread.node, 'conpherence-selected', false);
138
    }
139
140
    JX.DOM.alterClass(node, 'conpherence-selected', true);
141
    JX.DOM.alterClass(node, 'hide-unread-count', true);
142
143
    _thread.node = node;
144
145
    var data = JX.Stratcom.getData(node);
146
    _thread.selected = data.threadID;
147
148
    if (update_page_data) {
149
      updatePageData(data);
150
    }
151
152
    redrawThread();
153
  }
154
155
  function updatePageData(data) {
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
156
    var uri = '/Z' + _thread.selected;
157
    JX.History.replace(uri);
1 by Richard Sellam
Import upstream version 0~git20141023
158
    if (data.title) {
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
159
      JX.Title.setTitle(data.title);
1 by Richard Sellam
Import upstream version 0~git20141023
160
    } else if (_thread.node) {
161
      var threadData = JX.Stratcom.getData(_thread.node);
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
162
      JX.Title.setTitle(threadData.title);
1 by Richard Sellam
Import upstream version 0~git20141023
163
    }
164
  }
165
166
  JX.Stratcom.listen(
167
    'conpherence-update-page-data',
168
    null,
169
    function (e) {
170
      updatePageData(e.getData());
171
    }
172
  );
173
174
  function redrawThread() {
175
    if (!_thread.node) {
176
      return;
177
    }
178
179
    if (_thread.visible == _thread.selected) {
180
      return;
181
    }
182
183
    var data = JX.Stratcom.getData(_thread.node);
184
185
    if (_thread.visible !== null || !config.hasThread) {
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
186
      threadManager.setLoadThreadURI('/conpherence/' + data.threadID + '/');
187
      threadManager.loadThreadByID(data.threadID);
1 by Richard Sellam
Import upstream version 0~git20141023
188
    } else if (config.hasThread) {
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
189
      // we loaded the thread via the server so let the thread manager know
190
      threadManager.setLoadedThreadID(config.selectedThreadID);
191
      threadManager.setLoadedThreadPHID(config.selectedThreadPHID);
192
      threadManager.setLatestTransactionID(config.latestTransactionID);
193
      threadManager.setCanEditLoadedThread(config.canEditSelectedThread);
0.15.3 by Richard Sellam
Import upstream version 0~git20150525
194
      threadManager.cacheCurrentTransactions();
1 by Richard Sellam
Import upstream version 0~git20141023
195
      _scrollMessageWindow();
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
196
      _focusTextarea();
1 by Richard Sellam
Import upstream version 0~git20141023
197
    } else {
198
      didRedrawThread();
199
    }
200
201
    if (_thread.visible !== null || !config.hasWidgets) {
202
      reloadWidget(data);
203
    } else {
204
     JX.Stratcom.invoke(
205
      'conpherence-update-widgets',
206
      null,
207
      {
208
        widget : getDefaultWidget(),
209
        buildSelectors : false,
210
        toggleWidget : true,
211
        threadID : _thread.selected
212
      });
213
    }
214
215
    _thread.visible = _thread.selected;
216
  }
217
218
  function markThreadsLoading(loading) {
219
    var root = JX.DOM.find(document, 'div', 'conpherence-layout');
220
    var menu = JX.DOM.find(root, 'div', 'conpherence-menu-pane');
221
    JX.DOM.alterClass(menu, 'loading', loading);
222
  }
223
224
  function markThreadLoading(loading) {
225
    var root = JX.DOM.find(document, 'div', 'conpherence-layout');
226
    var header_root = JX.DOM.find(root, 'div', 'conpherence-header-pane');
227
    var messages_root = JX.DOM.find(root, 'div', 'conpherence-message-pane');
228
    var form_root = JX.DOM.find(root, 'div', 'conpherence-form');
229
230
    JX.DOM.alterClass(header_root, 'loading', loading);
231
    JX.DOM.alterClass(messages_root, 'loading', loading);
232
    JX.DOM.alterClass(form_root, 'loading', loading);
233
234
    try {
235
      var textarea = JX.DOM.find(form, 'textarea');
236
      textarea.disabled = loading;
237
      var button = JX.DOM.find(form, 'button');
238
      button.disabled = loading;
239
    } catch (ex) {
240
      // haven't loaded it yet!
241
    }
242
  }
243
244
  function markWidgetLoading(loading) {
245
    var root = JX.DOM.find(document, 'div', 'conpherence-layout');
246
    var widgets_root = JX.DOM.find(root, 'div', 'conpherence-widget-pane');
247
248
    JX.DOM.alterClass(widgets_root, 'loading', loading);
249
  }
250
251
  function reloadWidget(data) {
252
    markWidgetLoading(true);
253
    if (!data.widget) {
254
      data.widget = getDefaultWidget();
255
    }
256
    var widget_uri = config.baseURI + 'widget/' + data.threadID + '/';
257
    new JX.Workflow(widget_uri, {})
258
      .setHandler(JX.bind(null, onWidgetResponse, data.threadID, data.widget))
259
      .start();
260
  }
261
  JX.Stratcom.listen(
262
    'conpherence-reload-widget',
263
    null,
264
    function (e) {
265
      var data = e.getData();
266
      if (data.threadID != _thread.selected) {
267
        return;
268
      }
269
      reloadWidget(data);
270
    }
271
  );
272
273
  function onWidgetResponse(thread_id, widget, response) {
274
    // we got impatient and this is no longer the right answer :/
275
    if (_thread.selected != thread_id) {
276
      return;
277
    }
278
    var root = JX.DOM.find(document, 'div', 'conpherence-layout');
279
    var widgets_root = JX.DOM.find(root, 'div', 'conpherence-widgets-holder');
280
    JX.DOM.setContent(widgets_root, JX.$H(response.widgets));
281
282
    JX.Stratcom.invoke(
283
      'conpherence-update-widgets',
284
      null,
285
      {
286
        widget : widget,
287
        buildSelectors : true,
288
        toggleWidget : true,
289
        threadID : _thread.selected
290
      });
291
292
    markWidgetLoading(false);
293
  }
294
295
  function getDefaultWidget() {
296
    var device = JX.Device.getDevice();
297
    var widget = 'conpherence-message-pane';
298
    if (device == 'desktop') {
299
      widget = 'widgets-people';
0.15.4 by Richard Sellam
Import upstream version 0~git20150613
300
      var uri = JX.$U(location.href);
301
      var params = uri.getQueryParams();
302
      if ('settings' in params) {
303
        widget = 'widgets-settings';
304
      }
1 by Richard Sellam
Import upstream version 0~git20141023
305
    }
306
    return widget;
307
  }
308
309
  /**
310
   * This function is a wee bit tricky. Internally, we want to scroll the
311
   * message window and let other stuff - notably widgets - redraw / build if
312
   * necessary. Externally, we want a hook to scroll the message window
313
   * - notably when the widget selector is used to invoke the message pane.
314
   * The following three functions get 'er done.
315
   */
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
316
  function didRedrawThread(build_device_widget_selector) {
317
    _scrollMessageWindow();
318
    _focusTextarea();
319
    JX.Stratcom.invoke(
320
      'conpherence-did-redraw-thread',
321
      null,
322
      {
323
        widget : getDefaultWidget(),
324
        threadID : _thread.selected,
325
        buildDeviceWidgetSelector : build_device_widget_selector
326
      });
1 by Richard Sellam
Import upstream version 0~git20141023
327
  }
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
328
329
  var _firstScroll = true;
1 by Richard Sellam
Import upstream version 0~git20141023
330
  function _scrollMessageWindow() {
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
331
    if (_firstScroll) {
332
      _firstScroll = false;
333
334
      // We want to let the standard #anchor tech take over after we make sure
335
      // we don't have to present the user with a "load older message?" dialog
336
      if (window.location.hash) {
337
        var hash = window.location.hash.replace(/^#/, '');
338
        try {
339
          JX.$('anchor-' + hash);
340
        } catch (ex) {
341
          var uri = '/conpherence/' +
342
            _thread.selected + '/' + hash + '/';
343
          threadManager.setLoadThreadURI(uri);
344
          threadManager.loadThreadByID(_thread.selected, true);
345
          _firstScroll = true;
346
          return;
347
        }
348
        return;
349
      }
350
    }
351
    scrollbar.scrollTo(scrollbar.getViewportNode().scrollHeight);
352
  }
353
  function _focusTextarea() {
1 by Richard Sellam
Import upstream version 0~git20141023
354
    var root = JX.DOM.find(document, 'div', 'conpherence-layout');
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
355
    var form_root = JX.DOM.find(root, 'div', 'conpherence-form');
356
    try {
357
      var textarea = JX.DOM.find(form_root, 'textarea');
358
      // We may have a draft so do this JS trick so we end up focused at the
359
      // end of the draft.
360
      var textarea_value = textarea.value;
361
      textarea.value = '';
362
      JX.DOM.focus(textarea);
363
      textarea.value = textarea_value;
364
    } catch (ex) {
365
      // no textarea? no problem
366
    }
1 by Richard Sellam
Import upstream version 0~git20141023
367
  }
368
  JX.Stratcom.listen(
369
    'conpherence-redraw-thread',
370
    null,
371
    function () {
372
      _scrollMessageWindow();
373
    }
374
  );
375
376
  JX.Stratcom.listen(
377
    'click',
378
    'conpherence-menu-click',
379
    function(e) {
380
      if (!e.isNormalClick()) {
381
        return;
382
      }
383
384
      // On devices, just follow the link normally.
385
      if (JX.Device.getDevice() != 'desktop') {
386
        return;
387
      }
388
389
      e.kill();
390
      selectThread(e.getNode('conpherence-menu-click'), true);
391
    });
392
393
  JX.Stratcom.listen('click', 'conpherence-edit-metadata', function (e) {
394
    e.kill();
395
    var root = e.getNode('conpherence-layout');
396
    var form = JX.DOM.find(root, 'form', 'conpherence-pontificate');
397
    var data = e.getNodeData('conpherence-edit-metadata');
398
    var header = JX.DOM.find(root, 'div', 'conpherence-header-pane');
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
399
    var messages = scrollbar.getContentNode();
1 by Richard Sellam
Import upstream version 0~git20141023
400
401
    new JX.Workflow.newFromForm(form, data)
402
      .setHandler(JX.bind(this, function(r) {
403
        JX.DOM.appendContent(messages, JX.$H(r.transactions));
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
404
        _scrollMessageWindow();
1 by Richard Sellam
Import upstream version 0~git20141023
405
406
        JX.DOM.setContent(
407
          header,
408
          JX.$H(r.header)
409
        );
410
411
        try {
412
          // update the menu entry
413
          JX.DOM.replace(
414
            JX.$(r.conpherence_phid + '-nav-item'),
415
            JX.$H(r.nav_item)
416
          );
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
417
          selectThreadByID(r.conpherence_phid + '-nav-item');
1 by Richard Sellam
Import upstream version 0~git20141023
418
        } catch (ex) {
419
          // Ignore; this view may not have a menu.
420
        }
421
      }))
422
      .start();
423
  });
424
425
  /**
426
   * On devices, we just show a thread list, so we don't want to automatically
427
   * select or load any threads. On desktop, we automatically select the first
428
   * thread, changing the _currentRole from list to thread.
429
   */
430
  function onDeviceChange() {
431
    var new_device = JX.Device.getDevice();
432
    if (new_device === _oldDevice) {
433
      return;
434
    }
435
436
    if (_oldDevice === null) {
437
      _oldDevice = new_device;
438
      if (_currentRole == 'list') {
439
        if (new_device != 'desktop') {
440
          return;
441
        }
442
      } else {
443
        loadThreads();
444
        return;
445
      }
446
    }
447
    var update_toggled_widget =
448
      new_device == 'desktop' || _oldDevice == 'desktop';
449
    _oldDevice = new_device;
450
451
    if (_thread.visible !== null && update_toggled_widget) {
452
      JX.Stratcom.invoke(
453
        'conpherence-did-redraw-thread',
454
        null,
455
        {
456
          widget : getDefaultWidget(),
457
          threadID : _thread.selected
458
        });
459
    }
460
461
    if (_currentRole == 'list' && new_device == 'desktop') {
462
      // this selects a thread and loads it
463
      didLoadThreads();
464
      _currentRole = 'thread';
465
      var root = JX.DOM.find(document, 'div', 'conpherence-layout');
466
      JX.DOM.alterClass(root, 'conpherence-role-list', false);
467
      JX.DOM.alterClass(root, 'conpherence-role-thread', true);
468
    }
469
  }
470
  JX.Stratcom.listen('phabricator-device-change', null, onDeviceChange);
471
472
  function loadThreads() {
473
    markThreadsLoading(true);
474
    var uri = config.baseURI + 'thread/' + config.selectedThreadID + '/';
475
    new JX.Workflow(uri)
476
      .setHandler(onLoadThreadsResponse)
477
      .start();
478
  }
479
480
  function onLoadThreadsResponse(r) {
481
    var layout = JX.$(config.layoutID);
482
    var menu = JX.DOM.find(layout, 'div', 'conpherence-menu-pane');
483
    JX.DOM.setContent(menu, JX.$H(r));
484
485
    config.selectedID && selectThreadByID(config.selectedID);
486
487
    markThreadsLoading(false);
488
  }
489
490
  function didLoadThreads() {
491
    // If there's no thread selected yet, select the current thread or the
492
    // first thread.
493
    if (!_thread.selected) {
494
      if (config.selectedID) {
495
        selectThreadByID(config.selectedID, true);
496
      } else {
497
        var layout = JX.$(config.layoutID);
498
        var threads = JX.DOM.scry(layout, 'a', 'conpherence-menu-click');
499
        if (threads.length) {
500
          selectThread(threads[0]);
501
        } else {
502
          var nothreads = JX.DOM.find(layout, 'div', 'conpherence-no-threads');
503
          nothreads.style.display = 'block';
504
          markThreadLoading(false);
505
          markWidgetLoading(false);
506
        }
507
      }
508
    }
509
  }
510
511
  JX.Stratcom.listen(
512
    ['click'],
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
513
    'conpherence-menu-see-more',
514
    function (e) {
515
      e.kill();
516
      var sigil = e.getNodeData('conpherence-menu-see-more').moreSigil;
517
      var root = JX.$('conpherence-menu-pane');
518
      var more = JX.DOM.scry(root, 'li', sigil);
519
      for (var i = 0; i < more.length; i++) {
520
        JX.DOM.alterClass(more[i], 'hidden', false);
521
      }
522
      JX.DOM.hide(e.getNode('conpherence-menu-see-more'));
523
    });
1 by Richard Sellam
Import upstream version 0~git20141023
524
525
  JX.Stratcom.listen(
526
    ['keydown'],
527
    'conpherence-pontificate',
0.15.2 by Richard Sellam
Import upstream version 0~git20150428
528
    function (e) {
529
      threadManager.handleDraftKeydown(e);
530
    });
1 by Richard Sellam
Import upstream version 0~git20141023
531
532
});