[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/webroot/rsrc/js/application/conpherence/ -> behavior-menu.js (source)

   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
  11   *           phabricator-shaped-request
  12   */
  13  
  14  JX.behavior('conpherence-menu', function(config) {
  15  
  16    /**
  17     * State for displayed thread.
  18     */
  19    var _thread = {
  20      selected: null,
  21      visible: null,
  22      node: null
  23    };
  24  
  25    /**
  26     * Current role of this behavior. The two possible roles are to show a 'list'
  27     * of threads or a specific 'thread'. On devices, this behavior stays in the
  28     * 'list' role indefinitely, treating clicks normally and the next page
  29     * loads the behavior with role = 'thread'. On desktop, this behavior
  30     * auto-loads a thread as part of the 'list' role. As the thread loads the
  31     * role is changed to 'thread'.
  32     */
  33    var _currentRole = null;
  34  
  35    /**
  36     * When _oldDevice is null the code is executing for the first time.
  37     */
  38    var _oldDevice = null;
  39  
  40    /**
  41     * Initializes this behavior based on all the configuraton jonx and the
  42     * result of JX.Device.getDevice();
  43     */
  44    function init() {
  45      _currentRole = config.role;
  46  
  47      if (_currentRole == 'thread') {
  48        markThreadsLoading(true);
  49      } else {
  50        markThreadLoading(true);
  51      }
  52      markWidgetLoading(true);
  53      onDeviceChange();
  54    }
  55    init();
  56  
  57    /**
  58     * Selecting threads
  59     */
  60    JX.Stratcom.listen(
  61      'conpherence-selectthread',
  62      null,
  63      function (e) {
  64        selectThreadByID(e.getData().id);
  65      }
  66    );
  67  
  68    function selectThreadByID(id, update_page_data) {
  69      var thread = JX.$(id);
  70      selectThread(thread, update_page_data);
  71    }
  72  
  73    function selectThread(node, update_page_data) {
  74      if (_thread.node) {
  75        JX.DOM.alterClass(_thread.node, 'conpherence-selected', false);
  76        // keep the unread-count hidden still. big TODO once we ajax in updates
  77        // to threads to make this work right and move threads between read /
  78        // unread
  79      }
  80  
  81      JX.DOM.alterClass(node, 'conpherence-selected', true);
  82      JX.DOM.alterClass(node, 'hide-unread-count', true);
  83  
  84      _thread.node = node;
  85  
  86      var data = JX.Stratcom.getData(node);
  87      _thread.selected = data.threadID;
  88  
  89      if (update_page_data) {
  90        updatePageData(data);
  91      }
  92  
  93      redrawThread();
  94    }
  95  
  96    function updatePageData(data) {
  97      var uri_suffix = _thread.selected + '/';
  98      if (data.use_base_uri) {
  99        uri_suffix = '';
 100      }
 101      JX.History.replace(config.baseURI + uri_suffix);
 102      if (data.title) {
 103        document.title = data.title;
 104      } else if (_thread.node) {
 105        var threadData = JX.Stratcom.getData(_thread.node);
 106        document.title = threadData.title;
 107      }
 108    }
 109  
 110    JX.Stratcom.listen(
 111      'conpherence-update-page-data',
 112      null,
 113      function (e) {
 114        updatePageData(e.getData());
 115      }
 116    );
 117  
 118    function redrawThread() {
 119      if (!_thread.node) {
 120        return;
 121      }
 122  
 123      if (_thread.visible == _thread.selected) {
 124        return;
 125      }
 126  
 127      var data = JX.Stratcom.getData(_thread.node);
 128  
 129      if (_thread.visible !== null || !config.hasThread) {
 130        markThreadLoading(true);
 131        var uri = config.baseURI + data.threadID + '/';
 132        new JX.Workflow(uri, {})
 133          .setHandler(JX.bind(null, onLoadThreadResponse, data.threadID))
 134          .start();
 135      } else if (config.hasThread) {
 136        _scrollMessageWindow();
 137      } else {
 138        didRedrawThread();
 139      }
 140  
 141      if (_thread.visible !== null || !config.hasWidgets) {
 142        reloadWidget(data);
 143      } else {
 144       JX.Stratcom.invoke(
 145        'conpherence-update-widgets',
 146        null,
 147        {
 148          widget : getDefaultWidget(),
 149          buildSelectors : false,
 150          toggleWidget : true,
 151          threadID : _thread.selected
 152        });
 153      }
 154  
 155      _thread.visible = _thread.selected;
 156    }
 157  
 158    function markThreadsLoading(loading) {
 159      var root = JX.DOM.find(document, 'div', 'conpherence-layout');
 160      var menu = JX.DOM.find(root, 'div', 'conpherence-menu-pane');
 161      JX.DOM.alterClass(menu, 'loading', loading);
 162    }
 163  
 164    function markThreadLoading(loading) {
 165      var root = JX.DOM.find(document, 'div', 'conpherence-layout');
 166      var header_root = JX.DOM.find(root, 'div', 'conpherence-header-pane');
 167      var messages_root = JX.DOM.find(root, 'div', 'conpherence-message-pane');
 168      var form_root = JX.DOM.find(root, 'div', 'conpherence-form');
 169  
 170      JX.DOM.alterClass(header_root, 'loading', loading);
 171      JX.DOM.alterClass(messages_root, 'loading', loading);
 172      JX.DOM.alterClass(form_root, 'loading', loading);
 173  
 174      try {
 175        var textarea = JX.DOM.find(form, 'textarea');
 176        textarea.disabled = loading;
 177        var button = JX.DOM.find(form, 'button');
 178        button.disabled = loading;
 179      } catch (ex) {
 180        // haven't loaded it yet!
 181      }
 182    }
 183  
 184    function markWidgetLoading(loading) {
 185      var root = JX.DOM.find(document, 'div', 'conpherence-layout');
 186      var widgets_root = JX.DOM.find(root, 'div', 'conpherence-widget-pane');
 187  
 188      JX.DOM.alterClass(widgets_root, 'loading', loading);
 189    }
 190  
 191    function reloadWidget(data) {
 192      markWidgetLoading(true);
 193      if (!data.widget) {
 194        data.widget = getDefaultWidget();
 195      }
 196      var widget_uri = config.baseURI + 'widget/' + data.threadID + '/';
 197      new JX.Workflow(widget_uri, {})
 198        .setHandler(JX.bind(null, onWidgetResponse, data.threadID, data.widget))
 199        .start();
 200    }
 201    JX.Stratcom.listen(
 202      'conpherence-reload-widget',
 203      null,
 204      function (e) {
 205        var data = e.getData();
 206        if (data.threadID != _thread.selected) {
 207          return;
 208        }
 209        reloadWidget(data);
 210      }
 211    );
 212  
 213    function onWidgetResponse(thread_id, widget, response) {
 214      // we got impatient and this is no longer the right answer :/
 215      if (_thread.selected != thread_id) {
 216        return;
 217      }
 218      var root = JX.DOM.find(document, 'div', 'conpherence-layout');
 219      var widgets_root = JX.DOM.find(root, 'div', 'conpherence-widgets-holder');
 220      JX.DOM.setContent(widgets_root, JX.$H(response.widgets));
 221  
 222      JX.Stratcom.invoke(
 223        'conpherence-update-widgets',
 224        null,
 225        {
 226          widget : widget,
 227          buildSelectors : true,
 228          toggleWidget : true,
 229          threadID : _thread.selected
 230        });
 231  
 232      markWidgetLoading(false);
 233    }
 234  
 235    function getDefaultWidget() {
 236      var device = JX.Device.getDevice();
 237      var widget = 'conpherence-message-pane';
 238      if (device == 'desktop') {
 239        widget = 'widgets-people';
 240      }
 241      return widget;
 242    }
 243  
 244    function onLoadThreadResponse(thread_id, response) {
 245      // we got impatient and this is no longer the right answer :/
 246      if (_thread.selected != thread_id) {
 247        return;
 248      }
 249      var header = JX.$H(response.header);
 250      var messages = JX.$H(response.messages);
 251      var form = JX.$H(response.form);
 252      var root = JX.DOM.find(document, 'div', 'conpherence-layout');
 253      var header_root = JX.DOM.find(root, 'div', 'conpherence-header-pane');
 254      var messages_root = JX.DOM.find(root, 'div', 'conpherence-messages');
 255      var form_root = JX.DOM.find(root, 'div', 'conpherence-form');
 256      JX.DOM.setContent(header_root, header);
 257      JX.DOM.setContent(messages_root, messages);
 258      JX.DOM.setContent(form_root, form);
 259  
 260      markThreadLoading(false);
 261  
 262      didRedrawThread(true);
 263    }
 264  
 265    /**
 266     * This function is a wee bit tricky. Internally, we want to scroll the
 267     * message window and let other stuff - notably widgets - redraw / build if
 268     * necessary. Externally, we want a hook to scroll the message window
 269     * - notably when the widget selector is used to invoke the message pane.
 270     * The following three functions get 'er done.
 271     */
 272     function didRedrawThread(build_device_widget_selector) {
 273       _scrollMessageWindow();
 274       JX.Stratcom.invoke(
 275         'conpherence-did-redraw-thread',
 276         null,
 277         {
 278           widget : getDefaultWidget(),
 279           threadID : _thread.selected,
 280           buildDeviceWidgetSelector : build_device_widget_selector
 281         });
 282    }
 283    function _scrollMessageWindow() {
 284      var root = JX.DOM.find(document, 'div', 'conpherence-layout');
 285      var messages_root = JX.DOM.find(root, 'div', 'conpherence-messages');
 286      messages_root.scrollTop = messages_root.scrollHeight;
 287    }
 288    JX.Stratcom.listen(
 289      'conpherence-redraw-thread',
 290      null,
 291      function () {
 292        _scrollMessageWindow();
 293      }
 294    );
 295  
 296    JX.Stratcom.listen(
 297      'click',
 298      'conpherence-menu-click',
 299      function(e) {
 300        if (!e.isNormalClick()) {
 301          return;
 302        }
 303  
 304        // On devices, just follow the link normally.
 305        if (JX.Device.getDevice() != 'desktop') {
 306          return;
 307        }
 308  
 309        e.kill();
 310        selectThread(e.getNode('conpherence-menu-click'), true);
 311      });
 312  
 313    JX.Stratcom.listen('click', 'conpherence-edit-metadata', function (e) {
 314      e.kill();
 315      var root = e.getNode('conpherence-layout');
 316      var form = JX.DOM.find(root, 'form', 'conpherence-pontificate');
 317      var data = e.getNodeData('conpherence-edit-metadata');
 318      var header = JX.DOM.find(root, 'div', 'conpherence-header-pane');
 319      var messages = JX.DOM.find(root, 'div', 'conpherence-messages');
 320  
 321      new JX.Workflow.newFromForm(form, data)
 322        .setHandler(JX.bind(this, function(r) {
 323          JX.DOM.appendContent(messages, JX.$H(r.transactions));
 324          messages.scrollTop = messages.scrollHeight;
 325  
 326          JX.DOM.setContent(
 327            header,
 328            JX.$H(r.header)
 329          );
 330  
 331          try {
 332            // update the menu entry
 333            JX.DOM.replace(
 334              JX.$(r.conpherence_phid + '-nav-item'),
 335              JX.$H(r.nav_item)
 336            );
 337            JX.Stratcom.invoke(
 338              'conpherence-selectthread',
 339              null,
 340              { id : r.conpherence_phid + '-nav-item' }
 341            );
 342          } catch (ex) {
 343            // Ignore; this view may not have a menu.
 344          }
 345        }))
 346        .start();
 347    });
 348  
 349    var _loadingTransactionID = null;
 350    JX.Stratcom.listen('click', 'show-older-messages', function(e) {
 351      e.kill();
 352      var data = e.getNodeData('show-older-messages');
 353      if (data.oldest_transaction_id == _loadingTransactionID) {
 354        return;
 355      }
 356      _loadingTransactionID = data.oldest_transaction_id;
 357      var node = e.getNode('show-older-messages');
 358      JX.DOM.setContent(node, 'Loading...');
 359      JX.DOM.alterClass(node, 'conpherence-show-older-messages-loading', true);
 360  
 361      var conf_id = _thread.selected;
 362      var root = JX.DOM.find(document, 'div', 'conpherence-layout');
 363      var messages_root = JX.DOM.find(root, 'div', 'conpherence-messages');
 364      new JX.Workflow(config.baseURI + conf_id + '/', data)
 365      .setHandler(function(r) {
 366        JX.DOM.remove(node);
 367        var messages = JX.$H(r.messages);
 368        JX.DOM.prependContent(
 369          messages_root,
 370          JX.$H(messages));
 371      }).start();
 372    });
 373  
 374    /**
 375     * On devices, we just show a thread list, so we don't want to automatically
 376     * select or load any threads. On desktop, we automatically select the first
 377     * thread, changing the _currentRole from list to thread.
 378     */
 379    function onDeviceChange() {
 380      var new_device = JX.Device.getDevice();
 381      if (new_device === _oldDevice) {
 382        return;
 383      }
 384  
 385      if (_oldDevice === null) {
 386        _oldDevice = new_device;
 387        if (_currentRole == 'list') {
 388          if (new_device != 'desktop') {
 389            return;
 390          }
 391        } else {
 392          loadThreads();
 393          return;
 394        }
 395      }
 396      var update_toggled_widget =
 397        new_device == 'desktop' || _oldDevice == 'desktop';
 398      _oldDevice = new_device;
 399  
 400      if (_thread.visible !== null && update_toggled_widget) {
 401        JX.Stratcom.invoke(
 402          'conpherence-did-redraw-thread',
 403          null,
 404          {
 405            widget : getDefaultWidget(),
 406            threadID : _thread.selected
 407          });
 408      }
 409  
 410      if (_currentRole == 'list' && new_device == 'desktop') {
 411        // this selects a thread and loads it
 412        didLoadThreads();
 413        _currentRole = 'thread';
 414        var root = JX.DOM.find(document, 'div', 'conpherence-layout');
 415        JX.DOM.alterClass(root, 'conpherence-role-list', false);
 416        JX.DOM.alterClass(root, 'conpherence-role-thread', true);
 417      }
 418    }
 419    JX.Stratcom.listen('phabricator-device-change', null, onDeviceChange);
 420  
 421    function loadThreads() {
 422      markThreadsLoading(true);
 423      var uri = config.baseURI + 'thread/' + config.selectedThreadID + '/';
 424      new JX.Workflow(uri)
 425        .setHandler(onLoadThreadsResponse)
 426        .start();
 427    }
 428  
 429    function onLoadThreadsResponse(r) {
 430      var layout = JX.$(config.layoutID);
 431      var menu = JX.DOM.find(layout, 'div', 'conpherence-menu-pane');
 432      JX.DOM.setContent(menu, JX.$H(r));
 433  
 434      config.selectedID && selectThreadByID(config.selectedID);
 435  
 436      _thread.node.scrollIntoView();
 437  
 438      markThreadsLoading(false);
 439    }
 440  
 441    function didLoadThreads() {
 442      // If there's no thread selected yet, select the current thread or the
 443      // first thread.
 444      if (!_thread.selected) {
 445        if (config.selectedID) {
 446          selectThreadByID(config.selectedID, true);
 447        } else {
 448          var layout = JX.$(config.layoutID);
 449          var threads = JX.DOM.scry(layout, 'a', 'conpherence-menu-click');
 450          if (threads.length) {
 451            selectThread(threads[0]);
 452          } else {
 453            var nothreads = JX.DOM.find(layout, 'div', 'conpherence-no-threads');
 454            nothreads.style.display = 'block';
 455            markThreadLoading(false);
 456            markWidgetLoading(false);
 457          }
 458        }
 459      }
 460    }
 461  
 462    var handleThreadScrollers = function (e) {
 463      e.kill();
 464  
 465      var data = e.getNodeData('conpherence-menu-scroller');
 466      var scroller = e.getNode('conpherence-menu-scroller');
 467      JX.DOM.alterClass(scroller, 'loading', true);
 468      JX.DOM.setContent(scroller.firstChild, 'Loading...');
 469      new JX.Workflow(scroller.href, data)
 470        .setHandler(
 471          JX.bind(null, threadScrollerResponse, scroller, data.direction))
 472        .start();
 473    };
 474  
 475    var threadScrollerResponse = function (scroller, direction, r) {
 476      var html = JX.$H(r.html);
 477  
 478      var thread_phids = r.phids;
 479      var reselect_id = null;
 480      // remove any threads that are in the list that we just got back
 481      // in the result set; things have changed and they'll be in the
 482      // right place soon
 483      for (var ii = 0; ii < thread_phids.length; ii++) {
 484        try {
 485          var node_id = thread_phids[ii] + '-nav-item';
 486          var node = JX.$(node_id);
 487          var node_data = JX.Stratcom.getData(node);
 488          if (node_data.id == _thread.selected) {
 489            reselect_id = node_id;
 490          }
 491          JX.DOM.remove(node);
 492        } catch (ex) {
 493          // ignore , just haven't seen this thread yet
 494        }
 495      }
 496  
 497      var root = JX.DOM.find(document, 'div', 'conpherence-layout');
 498      var menu_root = JX.DOM.find(root, 'div', 'conpherence-menu-pane');
 499      var scroll_y = 0;
 500      // we have to do some hyjinx in the up case to make the menu scroll to
 501      // where it should
 502      if (direction == 'up') {
 503        var style = {
 504          position: 'absolute',
 505          left:     '-10000px'
 506        };
 507        var test_size = JX.$N('div', {style: style}, html);
 508        document.body.appendChild(test_size);
 509        var html_size = JX.Vector.getDim(test_size);
 510        JX.DOM.remove(test_size);
 511        scroll_y = html_size.y;
 512      }
 513      JX.DOM.replace(scroller, html);
 514      menu_root.scrollTop += scroll_y;
 515  
 516      if (reselect_id) {
 517        JX.Stratcom.invoke(
 518          'conpherence-selectthread',
 519          null,
 520          { id : reselect_id }
 521        );
 522      }
 523    };
 524  
 525    JX.Stratcom.listen(
 526      ['click'],
 527      'conpherence-menu-scroller',
 528      handleThreadScrollers
 529    );
 530  
 531    var onkeydownDraft = function (e) {
 532      var form = e.getNode('tag:form');
 533      var data = e.getNodeData('tag:form');
 534  
 535      if (!data.preview) {
 536        var uri = config.baseURI + 'update/' + _thread.selected + '/';
 537        data.preview = new JX.PhabricatorShapedRequest(
 538          uri,
 539          JX.bag,
 540          function () {
 541            var data = JX.DOM.convertFormToDictionary(form);
 542            data.action = 'draft';
 543            return data;
 544          });
 545      }
 546  
 547      data.preview.trigger();
 548    };
 549  
 550    JX.Stratcom.listen(
 551      ['keydown'],
 552      'conpherence-pontificate',
 553      onkeydownDraft);
 554  
 555  });


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1