Skip to content

Commit d54d00c

Browse files
committed
handle url(...) and quoted values
1 parent 228a780 commit d54d00c

File tree

7 files changed

+612
-30
lines changed

7 files changed

+612
-30
lines changed

src/generators/dom/visitors/Element/StyleAttribute.ts

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -118,51 +118,62 @@ function getStyleValue(chunks: Node[]) {
118118
const value: Node[] = [];
119119

120120
let inUrl = false;
121+
let quoteMark = null;
122+
let escaped = false;
121123

122124
while (chunks.length) {
123-
const chunk = chunks[0];
125+
const chunk = chunks.shift();
124126

125127
if (chunk.type === 'Text') {
126-
// TODO annoying special case — urls can contain semicolons
127-
128-
let index = chunk.data.indexOf(';');
128+
let c = 0;
129+
while (c < chunk.data.length) {
130+
const char = chunk.data[c];
131+
132+
if (escaped) {
133+
escaped = false;
134+
} else if (char === '\\') {
135+
escaped = true;
136+
} else if (char === quoteMark) {
137+
quoteMark === null;
138+
} else if (char === '"' || char === "'") {
139+
quoteMark = char;
140+
} else if (char === ')' && inUrl) {
141+
inUrl = false;
142+
} else if (char === 'u' && chunk.data.slice(c, c + 4) === 'url(') {
143+
inUrl = true;
144+
} else if (char === ';' && !inUrl && !quoteMark) {
145+
break;
146+
}
129147

130-
if (index === -1) {
131-
value.push(chunk);
132-
chunks.shift();
148+
c += 1;
133149
}
134150

135-
else {
136-
if (index > 0) {
137-
value.push({
138-
type: 'Text',
139-
start: chunk.start,
140-
end: chunk.start + index,
141-
data: chunk.data.slice(0, index)
142-
});
143-
}
144-
145-
while (/[;\s]/.test(chunk.data[index])) index += 1;
151+
if (c > 0) {
152+
value.push({
153+
type: 'Text',
154+
start: chunk.start,
155+
end: chunk.start + c,
156+
data: chunk.data.slice(0, c)
157+
});
158+
}
146159

147-
const remainingData = chunk.data.slice(index);
160+
while (/[;\s]/.test(chunk.data[c])) c += 1;
161+
const remainingData = chunk.data.slice(c);
148162

149-
if (remainingData) {
150-
chunks[0] = {
151-
start: chunk.start + index,
152-
end: chunk.end,
153-
type: 'Text',
154-
data: remainingData
155-
};
156-
} else {
157-
chunks.shift();
158-
}
163+
if (remainingData) {
164+
chunks.unshift({
165+
start: chunk.start + c,
166+
end: chunk.end,
167+
type: 'Text',
168+
data: remainingData
169+
});
159170

160171
break;
161172
}
162173
}
163174

164175
else {
165-
value.push(chunks.shift());
176+
value.push(chunk);
166177
}
167178
}
168179

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
function noop() {}
2+
3+
function assign(target) {
4+
var k,
5+
source,
6+
i = 1,
7+
len = arguments.length;
8+
for (; i < len; i++) {
9+
source = arguments[i];
10+
for (k in source) target[k] = source[k];
11+
}
12+
13+
return target;
14+
}
15+
16+
function insertNode(node, target, anchor) {
17+
target.insertBefore(node, anchor);
18+
}
19+
20+
function detachNode(node) {
21+
node.parentNode.removeChild(node);
22+
}
23+
24+
function createElement(name) {
25+
return document.createElement(name);
26+
}
27+
28+
function destroy(detach) {
29+
this.destroy = noop;
30+
this.fire('destroy');
31+
this.set = this.get = noop;
32+
33+
if (detach !== false) this._fragment.unmount();
34+
this._fragment.destroy();
35+
this._fragment = this._state = null;
36+
}
37+
38+
function differs(a, b) {
39+
return a !== b || ((a && typeof a === 'object') || typeof a === 'function');
40+
}
41+
42+
function dispatchObservers(component, group, changed, newState, oldState) {
43+
for (var key in group) {
44+
if (!changed[key]) continue;
45+
46+
var newValue = newState[key];
47+
var oldValue = oldState[key];
48+
49+
var callbacks = group[key];
50+
if (!callbacks) continue;
51+
52+
for (var i = 0; i < callbacks.length; i += 1) {
53+
var callback = callbacks[i];
54+
if (callback.__calling) continue;
55+
56+
callback.__calling = true;
57+
callback.call(component, newValue, oldValue);
58+
callback.__calling = false;
59+
}
60+
}
61+
}
62+
63+
function get(key) {
64+
return key ? this._state[key] : this._state;
65+
}
66+
67+
function fire(eventName, data) {
68+
var handlers =
69+
eventName in this._handlers && this._handlers[eventName].slice();
70+
if (!handlers) return;
71+
72+
for (var i = 0; i < handlers.length; i += 1) {
73+
handlers[i].call(this, data);
74+
}
75+
}
76+
77+
function observe(key, callback, options) {
78+
var group = options && options.defer
79+
? this._observers.post
80+
: this._observers.pre;
81+
82+
(group[key] || (group[key] = [])).push(callback);
83+
84+
if (!options || options.init !== false) {
85+
callback.__calling = true;
86+
callback.call(this, this._state[key]);
87+
callback.__calling = false;
88+
}
89+
90+
return {
91+
cancel: function() {
92+
var index = group[key].indexOf(callback);
93+
if (~index) group[key].splice(index, 1);
94+
}
95+
};
96+
}
97+
98+
function on(eventName, handler) {
99+
if (eventName === 'teardown') return this.on('destroy', handler);
100+
101+
var handlers = this._handlers[eventName] || (this._handlers[eventName] = []);
102+
handlers.push(handler);
103+
104+
return {
105+
cancel: function() {
106+
var index = handlers.indexOf(handler);
107+
if (~index) handlers.splice(index, 1);
108+
}
109+
};
110+
}
111+
112+
function set(newState) {
113+
this._set(assign({}, newState));
114+
if (this._root._lock) return;
115+
this._root._lock = true;
116+
callAll(this._root._beforecreate);
117+
callAll(this._root._oncreate);
118+
callAll(this._root._aftercreate);
119+
this._root._lock = false;
120+
}
121+
122+
function _set(newState) {
123+
var oldState = this._state,
124+
changed = {},
125+
dirty = false;
126+
127+
for (var key in newState) {
128+
if (differs(newState[key], oldState[key])) changed[key] = dirty = true;
129+
}
130+
if (!dirty) return;
131+
132+
this._state = assign({}, oldState, newState);
133+
this._recompute(changed, this._state, oldState, false);
134+
if (this._bind) this._bind(changed, this._state);
135+
dispatchObservers(this, this._observers.pre, changed, this._state, oldState);
136+
this._fragment.update(changed, this._state);
137+
dispatchObservers(this, this._observers.post, changed, this._state, oldState);
138+
}
139+
140+
function callAll(fns) {
141+
while (fns && fns.length) fns.pop()();
142+
}
143+
144+
var proto = {
145+
destroy: destroy,
146+
get: get,
147+
fire: fire,
148+
observe: observe,
149+
on: on,
150+
set: set,
151+
teardown: destroy,
152+
_recompute: noop,
153+
_set: _set
154+
};
155+
156+
function create_main_fragment ( state, component ) {
157+
var div;
158+
159+
return {
160+
create: function () {
161+
div = createElement( 'div' );
162+
this.hydrate();
163+
},
164+
165+
hydrate: function ( nodes ) {
166+
div.style.setProperty('background', "url(data:image/png;base64," + state.data + ")");
167+
},
168+
169+
mount: function ( target, anchor ) {
170+
insertNode( div, target, anchor );
171+
},
172+
173+
update: function ( changed, state ) {
174+
if ( changed.data ) {
175+
div.style.setProperty('background', "url(data:image/png;base64," + state.data + ")");
176+
}
177+
},
178+
179+
unmount: function () {
180+
detachNode( div );
181+
},
182+
183+
destroy: noop
184+
};
185+
}
186+
187+
function SvelteComponent ( options ) {
188+
this.options = options;
189+
this._state = options.data || {};
190+
191+
this._observers = {
192+
pre: Object.create( null ),
193+
post: Object.create( null )
194+
};
195+
196+
this._handlers = Object.create( null );
197+
198+
this._root = options._root || this;
199+
this._yield = options._yield;
200+
this._bind = options._bind;
201+
202+
this._fragment = create_main_fragment( this._state, this );
203+
204+
if ( options.target ) {
205+
this._fragment.create();
206+
this._fragment.mount( options.target, options.anchor || null );
207+
}
208+
}
209+
210+
assign( SvelteComponent.prototype, proto );
211+
212+
export default SvelteComponent;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { assign, createElement, detachNode, insertNode, noop, proto } from "svelte/shared.js";
2+
3+
function create_main_fragment ( state, component ) {
4+
var div;
5+
6+
return {
7+
create: function () {
8+
div = createElement( 'div' );
9+
this.hydrate();
10+
},
11+
12+
hydrate: function ( nodes ) {
13+
div.style.setProperty('background', "url(data:image/png;base64," + state.data + ")");
14+
},
15+
16+
mount: function ( target, anchor ) {
17+
insertNode( div, target, anchor );
18+
},
19+
20+
update: function ( changed, state ) {
21+
if ( changed.data ) {
22+
div.style.setProperty('background', "url(data:image/png;base64," + state.data + ")");
23+
}
24+
},
25+
26+
unmount: function () {
27+
detachNode( div );
28+
},
29+
30+
destroy: noop
31+
};
32+
}
33+
34+
function SvelteComponent ( options ) {
35+
this.options = options;
36+
this._state = options.data || {};
37+
38+
this._observers = {
39+
pre: Object.create( null ),
40+
post: Object.create( null )
41+
};
42+
43+
this._handlers = Object.create( null );
44+
45+
this._root = options._root || this;
46+
this._yield = options._yield;
47+
this._bind = options._bind;
48+
49+
this._fragment = create_main_fragment( this._state, this );
50+
51+
if ( options.target ) {
52+
this._fragment.create();
53+
this._fragment.mount( options.target, options.anchor || null );
54+
}
55+
}
56+
57+
assign( SvelteComponent.prototype, proto );
58+
59+
export default SvelteComponent;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div style='background: url(data:image/png;base64,{{data}})'></div>

0 commit comments

Comments
 (0)