[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/webroot/rsrc/externals/javelin/ext/reactor/dom/ -> RDOM.js (source)

   1  /**
   2   * Javelin Reactive functions to work with the DOM.
   3   * @provides javelin-reactor-dom
   4   * @requires javelin-dom
   5   *           javelin-dynval
   6   *           javelin-reactor
   7   *           javelin-reactornode
   8   *           javelin-install
   9   *           javelin-util
  10   * @javelin
  11   */
  12  JX.install('RDOM', {
  13    statics : {
  14      _time : null,
  15      /**
  16       * DynVal of the current time in milliseconds.
  17       */
  18      time : function() {
  19        if (JX.RDOM._time === null) {
  20          var time = new JX.ReactorNode([], JX.id);
  21          window.setInterval(function() {
  22            time.forceSendValue(JX.now());
  23          }, 100);
  24          JX.RDOM._time = new JX.DynVal(time, JX.now());
  25        }
  26        return JX.RDOM._time;
  27      },
  28  
  29      /**
  30       * Given a DynVal[String], return a DOM text node whose value tracks it.
  31       */
  32      $DT : function(dyn_string) {
  33        var node = document.createTextNode(dyn_string.getValueNow());
  34        dyn_string.transform(function(s) { node.data = s; });
  35        return node;
  36      },
  37  
  38      _recvEventPulses : function(node, event) {
  39        var reactor_node = new JX.ReactorNode([], JX.id);
  40        var no_path = null;
  41        JX.DOM.listen(
  42          node,
  43          event,
  44          no_path,
  45          JX.bind(reactor_node, reactor_node.forceSendValue)
  46        );
  47  
  48        reactor_node.setGraphID(JX.DOM.uniqID(node));
  49        return reactor_node;
  50      },
  51  
  52      _recvChangePulses : function(node) {
  53        return JX.RDOM._recvEventPulses(node, 'change').transform(function() {
  54          return node.value;
  55        });
  56      },
  57  
  58  
  59      /**
  60       * Sets up a bidirectional DynVal for a node.
  61       * @param node :: DOM Node
  62       * @param inPulsesFn :: DOM Node -> ReactorNode
  63       * @param inDynValFn :: DOM Node -> ReactorNode -> DynVal
  64       * @param outFn :: ReactorNode -> DOM Node
  65       */
  66      _bidi : function(node, inPulsesFn, inDynValFn, outFn) {
  67        var inPulses = inPulsesFn(node);
  68        var inDynVal = inDynValFn(node, inPulses);
  69        outFn(inDynVal.getChanges(), node);
  70        inDynVal.getChanges().listen(inPulses);
  71        return inDynVal;
  72      },
  73  
  74      /**
  75       * ReactorNode[String] of the incoming values of a radio group.
  76       * @param Array of DOM elements, all the radio buttons in a group.
  77       */
  78      _recvRadioPulses : function(buttons) {
  79        var ins = [];
  80        for (var ii = 0; ii < buttons.length; ii++) {
  81          ins.push(JX.RDOM._recvChangePulses(buttons[ii]));
  82        }
  83        return new JX.ReactorNode(ins, JX.id);
  84      },
  85  
  86      /**
  87       * DynVal[String] of the incoming values of a radio group.
  88       * pulses is a ReactorNode[String] of the incoming values of the group
  89       */
  90      _recvRadio : function(buttons, pulses) {
  91        var init = '';
  92        for (var ii = 0; ii < buttons.length; ii++) {
  93          if (buttons[ii].checked) {
  94            init = buttons[ii].value;
  95            break;
  96          }
  97        }
  98  
  99        return new JX.DynVal(pulses, init);
 100      },
 101  
 102      /**
 103       * Send the pulses from the ReactorNode[String] to the radio group.
 104       * Sending an invalid value will result in a log message in __DEV__.
 105       */
 106      _sendRadioPulses : function(rnode, buttons) {
 107        return rnode.transform(function(val) {
 108          var found;
 109          if (__DEV__) {
 110            found = false;
 111          }
 112  
 113          for (var ii = 0; ii < buttons.length; ii++) {
 114            if (buttons[ii].value == val) {
 115              buttons[ii].checked = true;
 116              if (__DEV__) {
 117                found = true;
 118              }
 119            }
 120          }
 121  
 122          if (__DEV__) {
 123            if (!found) {
 124              throw new Error("Mismatched radio button value");
 125            }
 126          }
 127        });
 128      },
 129  
 130      /**
 131       * Bidirectional DynVal[String] for a radio group.
 132       * Sending an invalid value will result in a log message in __DEV__.
 133       */
 134      radio : function(input) {
 135        return JX.RDOM._bidi(
 136          input,
 137          JX.RDOM._recvRadioPulses,
 138          JX.RDOM._recvRadio,
 139          JX.RDOM._sendRadioPulses
 140        );
 141      },
 142  
 143      /**
 144       * ReactorNode[Boolean] of the values of the checkbox when it changes.
 145       */
 146      _recvCheckboxPulses : function(checkbox) {
 147        return JX.RDOM._recvChangePulses(checkbox).transform(function(val) {
 148          return Boolean(val);
 149        });
 150      },
 151  
 152      /**
 153       * DynVal[Boolean] of the value of a checkbox.
 154       */
 155      _recvCheckbox : function(checkbox, pulses) {
 156        return new JX.DynVal(pulses, Boolean(checkbox.checked));
 157      },
 158  
 159      /**
 160       * Send the pulses from the ReactorNode[Boolean] to the checkbox
 161       */
 162      _sendCheckboxPulses : function(rnode, checkbox) {
 163        return rnode.transform(function(val) {
 164          if (__DEV__) {
 165            if (!(val === true || val === false)) {
 166              throw new Error("Send boolean values to checkboxes.");
 167            }
 168          }
 169  
 170          checkbox.checked = val;
 171        });
 172      },
 173  
 174      /**
 175       * Bidirectional DynVal[Boolean] for a checkbox.
 176       */
 177      checkbox : function(input) {
 178        return JX.RDOM._bidi(
 179          input,
 180          JX.RDOM._recvCheckboxPulses,
 181          JX.RDOM._recvCheckbox,
 182          JX.RDOM._sendCheckboxPulses
 183        );
 184      },
 185  
 186      /**
 187       * ReactorNode[String] of the changing values of a text input.
 188       */
 189      _recvInputPulses : function(input) {
 190        // This misses advanced changes like paste events.
 191        var live_changes = [
 192          JX.RDOM._recvChangePulses(input),
 193          JX.RDOM._recvEventPulses(input, 'keyup'),
 194          JX.RDOM._recvEventPulses(input, 'keypress'),
 195          JX.RDOM._recvEventPulses(input, 'keydown')
 196        ];
 197  
 198        return new JX.ReactorNode(live_changes, function() {
 199          return input.value;
 200        });
 201      },
 202  
 203      /**
 204       * DynVal[String] of the value of a text input.
 205       */
 206      _recvInput : function(input, pulses) {
 207        return new JX.DynVal(pulses, input.value);
 208      },
 209  
 210      /**
 211       * Send the pulses from the ReactorNode[String] to the input
 212       */
 213      _sendInputPulses : function(rnode, input) {
 214        var result = rnode.transform(function(val) {
 215          input.value = val;
 216        });
 217        result.setGraphID(JX.DOM.uniqID(input));
 218        return result;
 219      },
 220  
 221  
 222      /**
 223       * Bidirectional DynVal[String] for a text input.
 224       */
 225      input : function(input) {
 226        return JX.RDOM._bidi(
 227          input,
 228          JX.RDOM._recvInputPulses,
 229          JX.RDOM._recvInput,
 230          JX.RDOM._sendInputPulses
 231        );
 232      },
 233  
 234      /**
 235       * ReactorNode[String] of the incoming changes in value of a select element.
 236       */
 237      _recvSelectPulses : function(select) {
 238        return JX.RDOM._recvChangePulses(select);
 239      },
 240  
 241      /**
 242       * DynVal[String] of the value of a select element.
 243       */
 244      _recvSelect : function(select, pulses) {
 245        return new JX.DynVal(pulses, select.value);
 246      },
 247  
 248      /**
 249       * Send the pulses from the ReactorNode[String] to the select.
 250       * Sending an invalid value will result in a log message in __DEV__.
 251       */
 252      _sendSelectPulses : function(rnode, select) {
 253        return rnode.transform(function(val) {
 254          select.value = val;
 255  
 256          if (__DEV__) {
 257            if (select.value !== val) {
 258              throw new Error("Mismatched select value");
 259            }
 260          }
 261        });
 262      },
 263  
 264      /**
 265       * Bidirectional DynVal[String] for the value of a select.
 266       */
 267      select : function(select) {
 268        return JX.RDOM._bidi(
 269          select,
 270          JX.RDOM._recvSelectPulses,
 271          JX.RDOM._recvSelect,
 272          JX.RDOM._sendSelectPulses
 273        );
 274      },
 275  
 276      /**
 277       * ReactorNode[undefined] that fires when a button is clicked.
 278       */
 279      clickPulses : function(button) {
 280        return JX.RDOM._recvEventPulses(button, 'click').transform(function() {
 281          return null;
 282        });
 283      },
 284  
 285      /**
 286       * ReactorNode[Boolean] of whether the mouse is over a target.
 287       */
 288      _recvIsMouseOverPulses : function(target) {
 289        var mouseovers = JX.RDOM._recvEventPulses(target, 'mouseover').transform(
 290          function() {
 291            return true;
 292          });
 293        var mouseouts = JX.RDOM._recvEventPulses(target, 'mouseout').transform(
 294          function() {
 295            return false;
 296          });
 297  
 298        return new JX.ReactorNode([mouseovers, mouseouts], JX.id);
 299      },
 300  
 301      /**
 302       * DynVal[Boolean] of whether the mouse is over a target.
 303       */
 304      isMouseOver : function(target) {
 305        // Not worth it to initialize this properly.
 306        return new JX.DynVal(JX.RDOM._recvIsMouseOverPulses(target), false);
 307      },
 308  
 309      /**
 310       * ReactorNode[Boolean] of whether an element has the focus.
 311       */
 312      _recvHasFocusPulses : function(target) {
 313        var focuses = JX.RDOM._recvEventPulses(target, 'focus').transform(
 314          function() {
 315            return true;
 316          });
 317        var blurs = JX.RDOM._recvEventPulses(target, 'blur').transform(
 318          function() {
 319            return false;
 320          });
 321  
 322        return new JX.ReactorNode([focuses, blurs], JX.id);
 323      },
 324  
 325      /**
 326       * DynVal[Boolean] of whether an element has the focus.
 327       */
 328      _recvHasFocus : function(target) {
 329        var is_focused_now = (target === document.activeElement);
 330        return new JX.DynVal(JX.RDOM._recvHasFocusPulses(target), is_focused_now);
 331      },
 332  
 333      _sendHasFocusPulses : function(rnode, target) {
 334        rnode.transform(function(should_focus) {
 335          if (should_focus) {
 336            target.focus();
 337          } else {
 338            target.blur();
 339          }
 340          return should_focus;
 341        });
 342      },
 343  
 344      /**
 345       * Bidirectional DynVal[Boolean] of whether an element has the focus.
 346       */
 347      hasFocus : function(target) {
 348        return JX.RDOM._bidi(
 349          target,
 350          JX.RDOM._recvHasFocusPulses,
 351          JX.RDOM._recvHasFocus,
 352          JX.RDOM._sendHasFocusPulses
 353        );
 354      },
 355  
 356      /**
 357       * Send a CSS class from a DynVal to a node
 358       */
 359      sendClass : function(dynval, node, className) {
 360        return dynval.transform(function(add) {
 361          JX.DOM.alterClass(node, className, add);
 362        });
 363      },
 364  
 365      /**
 366       * Dynamically attach a set of DynVals to a DOM node's properties as
 367       * specified by props.
 368       * props: {left: someDynVal, style: {backgroundColor: someOtherDynVal}}
 369       */
 370      sendProps : function(node, props) {
 371        var dynvals = [];
 372        var keys = [];
 373        var style_keys = [];
 374        for (var key in props) {
 375          keys.push(key);
 376          if (key === 'style') {
 377            for (var style_key in props[key]) {
 378              style_keys.push(style_key);
 379              dynvals.push(props[key][style_key]);
 380              node.style[style_key] = props[key][style_key].getValueNow();
 381            }
 382          } else {
 383            dynvals.push(props[key]);
 384            node[key] = props[key].getValueNow();
 385          }
 386        }
 387  
 388        return JX.Reactor.lift(JX.bind(null, function(keys, style_keys, node) {
 389          var args = JX.$A(arguments).slice(3);
 390  
 391          for (var ii = 0; ii < args.length; ii++) {
 392            if (keys[ii] === 'style') {
 393              for (var jj = 0; jj < style_keys.length; jj++) {
 394                node.style[style_keys[jj]] = args[ii];
 395                ii++;
 396              }
 397              ii--;
 398            } else {
 399              node[keys[ii]] = args[ii];
 400            }
 401          }
 402        }, keys, style_keys, node), dynvals);
 403      }
 404    }
 405  });


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