Skip to content

Commit a4121cd

Browse files
committed
initial commit
0 parents  commit a4121cd

28 files changed

+1809
-0
lines changed

.editorconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# EditorConfig is awesome: http://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
# Unix-style newlines with a newline ending every file
7+
[*]
8+
end_of_line = lf
9+
insert_final_newline = true
10+
11+
# 2 space indentation
12+
[**.*]
13+
indent_style = space
14+
indent_size = 2

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.rollupcache
2+
node_modules

.npmignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.vscode
2+
.rollupcache
3+
node_modules
4+
src
5+
test
6+
.editorconfig
7+
.gitignore
8+
.npmignore
9+
tsconfig.json
10+
tslint.json

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"typescript.tsdk": "node_modules/typescript/lib"
3+
}

README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
## Aurelia Style Binding Command Plugin
2+
3+
* Extends SyntaxInterpreter of default Binding Language implementation with commands `.style` (same with `.style-to-view`, `.style-one-way` but shorter), `.style-one-time`, `.style-two-way`, `.style-from-view` to bind to a single css rule of an element.
4+
5+
## Installation
6+
7+
* Install the dependency
8+
```
9+
npm install aurelia-style-binding-command-plugin
10+
```
11+
12+
* Import the module for side effect (auto extension)
13+
14+
```js
15+
// app-entry.js
16+
import 'aurelia-style-binding-command-plugin';
17+
18+
```
19+
20+
## Development
21+
22+
### Build the code
23+
24+
* After installing dependencies, run `build`
25+
26+
```
27+
npm run build
28+
```
29+
30+
### Test the code
31+
32+
```
33+
npm run test
34+
```
35+
36+
## How it works
37+
38+
* The `.style-*` binding commands instruct Aurelia to observe view model and assign new value to css property, like standard behavior of an Aurelia Binding. `.style-from-view` and `.style-two-way` work via `MutationObserver`, that detects change on `style` attribute of observed element.
39+
40+
## Possible extension
41+
42+
* Normal usage looks like this:
43+
44+
```html
45+
<div background-color.style='background'></div>
46+
```
47+
48+
* The following syntax could be made to work:
49+
50+
```html
51+
<div style.background-color='background'></div>
52+
```
53+
54+
The former is predictable, it follows the `[attribute-name].[binding-command]=[expression]` pattern that you see everywhere in an Aurelia application. The later is a more natural syntax for reading, as `background-color` is a property of `style` property of the element. If you have any argument why it should be supported, please file an issue.

dist/amd/index.js

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
define('aurelia-style-binding-command-plugin', ['exports', 'aurelia-binding', 'aurelia-pal', 'aurelia-templating-binding'], function (exports, aureliaBinding, aureliaPal, aureliaTemplatingBinding) { 'use strict';
2+
3+
var styleObserverContext = 'StyleObserver:context';
4+
var hyphenateCache = Object.create(null);
5+
var capitalMatcher = /([A-Z])/g;
6+
function addHyphenAndLower(char) {
7+
return '-' + char.toLowerCase();
8+
}
9+
function hyphenate(name) {
10+
if (name in hyphenateCache) {
11+
return hyphenateCache[name];
12+
}
13+
return hyphenateCache[name] = (name.charAt(0).toLowerCase() + name.slice(1)).replace(capitalMatcher, addHyphenAndLower);
14+
}
15+
16+
var InlineStyleObserver = /** @class */ (function () {
17+
function InlineStyleObserver(element, cssRule) {
18+
this.element = element;
19+
this.cssRule = cssRule;
20+
this.hyphenatedCssRule = hyphenate(cssRule);
21+
}
22+
InlineStyleObserver.prototype.getValue = function () {
23+
return this.element.style.getPropertyValue(this.hyphenatedCssRule);
24+
};
25+
InlineStyleObserver.prototype.setValue = function (newValue) {
26+
if (newValue !== this.value) {
27+
this.prevValue = this.value;
28+
this.value = newValue;
29+
this.element.style.setProperty(this.hyphenatedCssRule, this.value);
30+
this.notify();
31+
}
32+
};
33+
InlineStyleObserver.prototype.notify = function () {
34+
var prev = this.prevValue;
35+
var curr = this.value;
36+
this.callSubscribers(curr, prev);
37+
};
38+
InlineStyleObserver.prototype.syncValue = function () {
39+
var prev = this.value;
40+
var value = this.getValue();
41+
if (value !== prev) {
42+
this.prevValue = prev;
43+
this.value = value;
44+
this.notify();
45+
}
46+
};
47+
InlineStyleObserver.prototype.observeMutation = function () {
48+
var _this = this;
49+
if (!this.mo) {
50+
this.mo = aureliaPal.DOM.createMutationObserver(function () { return _this.syncValue(); });
51+
this.mo.observe(this.element, {
52+
attributes: true
53+
});
54+
}
55+
};
56+
InlineStyleObserver.prototype.unobserveMutation = function () {
57+
if (this.mo) {
58+
this.mo.disconnect();
59+
this.mo = null;
60+
}
61+
};
62+
InlineStyleObserver.prototype.subscribe = function (context, callable) {
63+
if (!this.hasSubscribers()) {
64+
this.observeMutation();
65+
}
66+
this.addSubscriber(context, callable);
67+
};
68+
InlineStyleObserver.prototype.unsubscribe = function (context, callable) {
69+
if (this.removeSubscriber(context, callable) && !this.hasSubscribers()) {
70+
this.unobserveMutation();
71+
}
72+
};
73+
return InlineStyleObserver;
74+
}());
75+
aureliaBinding.subscriberCollection()(InlineStyleObserver);
76+
77+
var StyleExpression = /** @class */ (function () {
78+
function StyleExpression(observerLocator, sourceExpression, targetProperty, mode, lookupFunctions) {
79+
this.observerLocator = observerLocator;
80+
this.sourceExpression = sourceExpression;
81+
this.targetProperty = targetProperty;
82+
this.mode = mode;
83+
this.lookupFunctions = lookupFunctions;
84+
}
85+
StyleExpression.prototype.createBinding = function (target) {
86+
return new StyleBinding(this.observerLocator, this.sourceExpression, target, this.targetProperty, this.mode, this.lookupFunctions);
87+
};
88+
return StyleExpression;
89+
}());
90+
var StyleBinding = /** @class */ (function () {
91+
function StyleBinding(observerLocator, sourceExpression, target, targetProperty, mode, lookupFunctions) {
92+
this.target = target;
93+
this.targetProperty = targetProperty;
94+
this.lookupFunctions = lookupFunctions;
95+
this.observerLocator = observerLocator;
96+
this.sourceExpression = sourceExpression;
97+
this.mode = mode;
98+
}
99+
StyleBinding.prototype.updateTarget = function (value) {
100+
this.styleObserver.setValue(value);
101+
};
102+
StyleBinding.prototype.updateSource = function (value) {
103+
this.sourceExpression.assign(this.source, value, this.lookupFunctions);
104+
};
105+
StyleBinding.prototype.call = function (context, newValue, oldValue) {
106+
if (!this.isBound) {
107+
return;
108+
}
109+
if (context === aureliaBinding.sourceContext) {
110+
oldValue = this.styleObserver.getValue();
111+
newValue = this.sourceExpression.evaluate(this.source, this.lookupFunctions);
112+
if (newValue !== oldValue) {
113+
this.updateTarget(newValue);
114+
}
115+
if (this.mode !== aureliaBinding.bindingMode.oneTime) {
116+
this._version++;
117+
this.sourceExpression.connect(this, this.source);
118+
this.unobserve(false);
119+
}
120+
return;
121+
}
122+
if (context === styleObserverContext) {
123+
if (newValue !== oldValue) {
124+
this.updateSource(newValue);
125+
}
126+
return;
127+
}
128+
throw new Error("Unexpected context for style binding: \"" + context + "\"");
129+
};
130+
StyleBinding.prototype.bind = function (source) {
131+
if (this.isBound) {
132+
if (this.source === source) {
133+
return;
134+
}
135+
this.unbind();
136+
}
137+
this.isBound = true;
138+
this.source = source;
139+
if (this.sourceExpression.bind) {
140+
this.sourceExpression.bind(this, source, this.lookupFunctions);
141+
}
142+
var styleObserver = this.target.__style_observer__;
143+
if (styleObserver) {
144+
this.styleObserver = styleObserver;
145+
}
146+
else {
147+
this.styleObserver = this.target.__style_observer__ = new InlineStyleObserver(this.target, this.targetProperty);
148+
}
149+
var mode = this.mode;
150+
if (mode !== aureliaBinding.bindingMode.fromView) {
151+
var value = this.sourceExpression.evaluate(source, this.lookupFunctions);
152+
this.updateTarget(value);
153+
}
154+
if (mode === aureliaBinding.bindingMode.oneTime) {
155+
return;
156+
}
157+
else if (mode === aureliaBinding.bindingMode.toView) {
158+
aureliaBinding.enqueueBindingConnect(this);
159+
}
160+
else if (mode === aureliaBinding.bindingMode.twoWay) {
161+
this.sourceExpression.connect(this, source);
162+
this.styleObserver.subscribe(styleObserverContext, this);
163+
}
164+
else if (mode === aureliaBinding.bindingMode.fromView) {
165+
this.styleObserver.subscribe(styleObserverContext, this);
166+
}
167+
};
168+
StyleBinding.prototype.unbind = function () {
169+
if (!this.isBound) {
170+
return;
171+
}
172+
this.isBound = false;
173+
if (this.sourceExpression.unbind) {
174+
this.sourceExpression.unbind(this, this.source);
175+
}
176+
this.source = null;
177+
this.styleObserver.unsubscribe(styleObserverContext, this);
178+
this.styleObserver = null;
179+
this.unobserve(true);
180+
};
181+
StyleBinding.prototype.connect = function (evaluate) {
182+
if (!this.isBound) {
183+
return;
184+
}
185+
if (evaluate) {
186+
var value = this.sourceExpression.evaluate(this.source, this.lookupFunctions);
187+
this.updateTarget(value);
188+
}
189+
this.sourceExpression.connect(this, this.source);
190+
};
191+
return StyleBinding;
192+
}());
193+
aureliaBinding.connectable()(StyleBinding);
194+
195+
var siProto = aureliaTemplatingBinding.SyntaxInterpreter.prototype;
196+
siProto.style = siProto['style-to-view'] = siProto['style-one-way'] = function (resources, element, info) {
197+
return new StyleExpression(this.observerLocator, this.parser.parse(info.attrValue), info.attrName, aureliaBinding.bindingMode.toView, resources.lookupFunctions);
198+
};
199+
siProto['style-one-time'] = function (resources, element, info) {
200+
return new StyleExpression(this.observerLocator, this.parser.parse(info.attrValue), info.attrName, aureliaBinding.bindingMode.oneTime, resources.lookupFunctions);
201+
};
202+
siProto['style-two-way'] = function (resources, element, info) {
203+
return new StyleExpression(this.observerLocator, this.parser.parse(info.attrValue), info.attrName, aureliaBinding.bindingMode.twoWay, resources.lookupFunctions);
204+
};
205+
siProto['style-from-view'] = function (resources, element, info) {
206+
return new StyleExpression(this.observerLocator, this.parser.parse(info.attrValue), info.attrName, aureliaBinding.bindingMode.fromView, resources.lookupFunctions);
207+
};
208+
209+
exports.InlineStyleObserver = InlineStyleObserver;
210+
exports.StyleExpression = StyleExpression;
211+
exports.StyleBinding = StyleBinding;
212+
213+
Object.defineProperty(exports, '__esModule', { value: true });
214+
215+
});

0 commit comments

Comments
 (0)