Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library template_binding.src.mustache_tokens; | |
| 6 | |
| 7 import 'package:observe/observe.dart'; | |
| 8 import 'package:template_binding/template_binding.dart'; | |
| 9 | |
| 10 /** | |
| 11 * Represents a set of parsed tokens from a {{ mustache binding expression }}. | |
| 12 * This can be created by calling [parse]. | |
| 13 * | |
| 14 * For performance reasons the data is stored in one linear array in [_tokens]. | |
| 15 * This class wraps that array and provides accessors in an attempt to make the | |
| 16 * pattern easier to understand. See [length] and [getText] for example. | |
| 17 */ | |
| 18 class MustacheTokens { | |
| 19 // Constants for indexing into the exploded structs in [_tokens] . | |
| 20 static const _TOKEN_TEXT = 0; | |
| 21 static const _TOKEN_ONETIME = 1; | |
| 22 static const _TOKEN_PATH = 2; | |
| 23 static const _TOKEN_PREPAREFN = 3; | |
| 24 static const _TOKEN_SIZE = 4; | |
| 25 | |
| 26 // There is 1 extra entry for the end text. | |
| 27 static const _TOKEN_ENDTEXT = 1; | |
| 28 | |
| 29 bool get hasOnePath => _tokens.length == _TOKEN_SIZE + _TOKEN_ENDTEXT; | |
| 30 bool get isSimplePath => hasOnePath && | |
| 31 _tokens[_TOKEN_TEXT] == '' && _tokens[_TOKEN_SIZE + _TOKEN_TEXT] == ''; | |
| 32 | |
| 33 /** | |
| 34 * [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one | |
| 35 * mustache. | |
| 36 */ | |
| 37 final List _tokens; | |
| 38 | |
| 39 final bool onlyOneTime; | |
| 40 | |
| 41 // Dart note: I think this is cached in JavaScript to avoid an extra | |
| 42 // allocation per template instance. Seems reasonable, so we do the same. | |
| 43 Function _combinator; | |
| 44 Function get combinator => _combinator; | |
| 45 | |
| 46 MustacheTokens._(this._tokens, this.onlyOneTime) { | |
| 47 // Should be: [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+]. | |
| 48 assert((_tokens.length - _TOKEN_ENDTEXT) % _TOKEN_SIZE == 0); | |
| 49 | |
| 50 _combinator = hasOnePath ? _singleCombinator : _listCombinator; | |
| 51 } | |
| 52 | |
| 53 int get length => _tokens.length ~/ _TOKEN_SIZE; | |
| 54 | |
| 55 /** | |
|
justinfagnani
2014/02/04 23:34:49
thanks!!
Jennifer Messerly
2014/02/04 23:40:41
yeah, thanks for the idea :). It made the code a l
| |
| 56 * Gets the [i]th text entry. Note that [length] can be passed to get the | |
| 57 * final text entry. | |
| 58 */ | |
| 59 String getText(int i) => _tokens[i * _TOKEN_SIZE + _TOKEN_TEXT]; | |
| 60 | |
| 61 /** Gets the oneTime flag for the [i]th token. */ | |
| 62 bool getOneTime(int i) => _tokens[i * _TOKEN_SIZE + _TOKEN_ONETIME]; | |
| 63 | |
| 64 /** Gets the path for the [i]th token. */ | |
| 65 PropertyPath getPath(int i) => _tokens[i * _TOKEN_SIZE + _TOKEN_PATH]; | |
| 66 | |
| 67 /** Gets the prepareBinding function for the [i]th token. */ | |
| 68 Function getPrepareBinding(int i) => | |
| 69 _tokens[i * _TOKEN_SIZE + _TOKEN_PREPAREFN]; | |
| 70 | |
| 71 | |
| 72 /** | |
| 73 * Parses {{ mustache }} bindings. | |
| 74 * | |
| 75 * Returns null if there are no matches. Otherwise returns the parsed tokens. | |
| 76 */ | |
| 77 static MustacheTokens parse(String s, String name, node, | |
| 78 BindingDelegate delegate) { | |
| 79 if (s == null || s.isEmpty) return null; | |
| 80 | |
| 81 var tokens = null; | |
| 82 var length = s.length; | |
| 83 var lastIndex = 0; | |
| 84 var onlyOneTime = true; | |
| 85 while (lastIndex < length) { | |
| 86 var startIndex = s.indexOf('{{', lastIndex); | |
| 87 var oneTimeStart = s.indexOf('[[', lastIndex); | |
| 88 var oneTime = false; | |
| 89 var terminator = '}}'; | |
| 90 | |
| 91 if (oneTimeStart >= 0 && | |
| 92 (startIndex < 0 || oneTimeStart < startIndex)) { | |
| 93 startIndex = oneTimeStart; | |
| 94 oneTime = true; | |
| 95 terminator = ']]'; | |
| 96 } | |
| 97 | |
| 98 var endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2) ; | |
|
justinfagnani
2014/02/04 23:34:49
long line
Jennifer Messerly
2014/02/04 23:40:41
Done.
| |
| 99 | |
| 100 if (endIndex < 0) { | |
| 101 if (tokens == null) return null; | |
| 102 | |
| 103 tokens.add(s.substring(lastIndex)); // TEXT | |
| 104 break; | |
| 105 } | |
| 106 | |
| 107 if (tokens == null) tokens = []; | |
| 108 tokens.add(s.substring(lastIndex, startIndex)); // TEXT | |
| 109 var pathString = s.substring(startIndex + 2, endIndex).trim(); | |
| 110 tokens.add(oneTime); // ONETIME? | |
| 111 onlyOneTime = onlyOneTime && oneTime; | |
| 112 tokens.add(new PropertyPath(pathString)); // PATH | |
| 113 var delegateFn = delegate == null ? null : | |
| 114 delegate.prepareBinding(pathString, name, node); | |
| 115 tokens.add(delegateFn); | |
| 116 | |
| 117 lastIndex = endIndex + 2; | |
| 118 } | |
| 119 | |
| 120 if (lastIndex == length) tokens.add(''); | |
| 121 | |
| 122 return new MustacheTokens._(tokens, onlyOneTime); | |
| 123 } | |
| 124 | |
| 125 | |
| 126 // Dart note: split "combinator" into the single/list variants, so the | |
| 127 // argument can be typed. | |
| 128 String _singleCombinator(Object value) { | |
| 129 if (value == null) value = ''; | |
| 130 return '${getText(0)}$value${getText(length)}'; | |
| 131 } | |
| 132 | |
| 133 String _listCombinator(List<Object> values) { | |
| 134 var newValue = new StringBuffer(getText(0)); | |
| 135 int len = this.length; | |
| 136 for (var i = 0; i < len; i++) { | |
| 137 var value = values[i]; | |
| 138 if (value != null) newValue.write(value); | |
| 139 newValue.write(getText(i + 1)); | |
| 140 } | |
| 141 return newValue.toString(); | |
| 142 } | |
| 143 } | |
| OLD | NEW |