Skip to content

Instantly share code, notes, and snippets.

@techird
Created January 27, 2015 04:55
Show Gist options
  • Save techird/7b6fde4285580a520f2d to your computer and use it in GitHub Desktop.
Save techird/7b6fde4285580a520f2d to your computer and use it in GitHub Desktop.
svg loader
define(function(require, exports, module) {
/*!
** Thenable -- Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
** Copyright (c) 2013-2014 Ralf S. Engelschall <http://engelschall.com>
** Licensed under The MIT License <http://opensource.org/licenses/MIT>
** Source-Code distributed on <http://github.com/rse/thenable>
*/
/* promise states [Promises/A+ 2.1] */
var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */
var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */
var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */
/* promise object constructor */
var Promise = function(executor) {
/* optionally support non-constructor/plain-function call */
if (!(this instanceof Promise))
return new Promise(executor);
/* initialize object */
this.id = 'Thenable/1.0.7';
this.state = STATE_PENDING; /* initial state */
this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */
this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */
this.onFulfilled = []; /* initial handlers */
this.onRejected = []; /* initial handlers */
/* support optional executor function */
if (typeof executor === 'function')
executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
};
/* Promise API methods */
Promise.prototype = {
/* promise resolving methods */
fulfill: function(value) { return deliver(this, STATE_FULFILLED, 'fulfillValue', value); },
reject: function(value) { return deliver(this, STATE_REJECTED, 'rejectReason', value); },
/* 'The then Method' [Promises/A+ 1.1, 1.2, 2.2] */
then: function(onFulfilled, onRejected) {
var curr = this;
var next = new Promise(); /* [Promises/A+ 2.2.7] */
curr.onFulfilled.push(
resolver(onFulfilled, next, 'fulfill')); /* [Promises/A+ 2.2.2/2.2.6] */
curr.onRejected.push(
resolver(onRejected, next, 'reject')); /* [Promises/A+ 2.2.3/2.2.6] */
execute(curr);
return next; /* [Promises/A+ 2.2.7, 3.3] */
}
};
/* deliver an action */
var deliver = function(curr, state, name, value) {
if (curr.state === STATE_PENDING) {
curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
curr[name] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
execute(curr);
}
return curr;
};
/* execute all handlers */
var execute = function(curr) {
if (curr.state === STATE_FULFILLED)
execute_handlers(curr, 'onFulfilled', curr.fulfillValue);
else if (curr.state === STATE_REJECTED)
execute_handlers(curr, 'onRejected', curr.rejectReason);
};
/* execute particular set of handlers */
var execute_handlers = function(curr, name, value) {
/* global process: true */
/* global setImmediate: true */
/* global setTimeout: true */
/* short-circuit processing */
if (curr[name].length === 0)
return;
/* iterate over all handlers, exactly once */
var handlers = curr[name];
curr[name] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
var func = function() {
for (var i = 0; i < handlers.length; i++)
handlers[i](value); /* [Promises/A+ 2.2.5] */
};
/* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */
if (typeof process === 'object' && typeof process.nextTick === 'function')
process.nextTick(func);
else if (typeof setImmediate === 'function')
setImmediate(func);
else
setTimeout(func, 0);
};
/* generate a resolver function */
var resolver = function(cb, next, method) {
return function(value) {
if (typeof cb !== 'function') /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
next[method].call(next, value); /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
else {
var result;
try {
if (value instanceof Promise) {
result = value.then(cb);
}
else result = cb(value);
} /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
catch (e) {
next.reject(e); /* [Promises/A+ 2.2.7.2] */
return;
}
resolve(next, result); /* [Promises/A+ 2.2.7.1] */
}
};
};
/* 'Promise Resolution Procedure' */ /* [Promises/A+ 2.3] */
var resolve = function(promise, x) {
/* sanity check arguments */ /* [Promises/A+ 2.3.1] */
if (promise === x) {
promise.reject(new TypeError('cannot resolve promise with itself'));
return;
}
/* surgically check for a 'then' method
(mainly to just call the 'getter' of 'then' only once) */
var then;
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try { then = x.then; } /* [Promises/A+ 2.3.3.1, 3.5] */
catch (e) {
promise.reject(e); /* [Promises/A+ 2.3.3.2] */
return;
}
}
/* handle own Thenables [Promises/A+ 2.3.2]
and similar 'thenables' [Promises/A+ 2.3.3] */
if (typeof then === 'function') {
var resolved = false;
try {
/* call retrieved 'then' method */ /* [Promises/A+ 2.3.3.3] */
then.call(x,
/* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */
function(y) {
if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */
if (y === x) /* [Promises/A+ 3.6] */
promise.reject(new TypeError('circular thenable chain'));
else
resolve(promise, y);
},
/* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */
function(r) {
if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */
promise.reject(r);
}
);
}
catch (e) {
if (!resolved) /* [Promises/A+ 2.3.3.3.3] */
promise.reject(e); /* [Promises/A+ 2.3.3.3.4] */
}
return;
}
/* handle other values */
promise.fulfill(x); /* [Promises/A+ 2.3.4, 2.3.3.4] */
};
Promise.resolve = function(value) {
return new Promise(function(resolve) {
resolve(value);
});
};
Promise.reject = function(reason) {
return new Promise(function(resolve, reject) {
reject(reason);
});
};
/* export API */
module.exports = Promise;
});
/**
* @fileOverview
*
* Convert SVG Dom Document to PNG DataURI
*
* @author: techird
* @copyright: Baidu FEX, 2014
*/
define(function(require, exports, module) {
var DomURL = window.URL || window.webkitURL || window;
function getSVGInfo(svg) {
var svgXml = new XMLSerializer().serializeToString(svg)
;
var width = parseInt(svg.getAttribute('width'));
var height = parseInt(svg.getAttribute('height'));
var blob = new Blob([svgXml], {
type: 'image/svg+xml'
});
var svgUrl = DomURL.createObjectURL(blob);
// var svgUrl = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgXml);
return {
width: width,
height: height,
dataUrl: svgUrl,
xml: svgXml
};
}
function convert(svg, callback) {
var info = getSVGInfo(svg);
var canvas = document.createElement('canvas');
canvas.width = info.width;
canvas.height = info.height;
var ctx = canvas.getContext('2d');
var img = document.createElement('img');
img.onload = function() {
ctx.drawImage(this, 0, 0);
callback(canvas.toDataURL('png'));
};
img.src = info.dataUrl;
}
module.exports = convert;
});
/* global $, console */
define(function(require, exports, module) {
var UNIT_PERFIX = 'u-';
var Promise = require('./promise');
var svg2png = require('./svg2png');
function fixNumber(n) {
return Math.round(n);
}
function makeBackground(target, svgXml) {
target.style.backgroundImage = 'url(data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgXml) + ')';
}
var guid = 0;
function load(url, container, processor) {
return new Promise(function(resolve, reject) {
$.ajax({
type: 'GET',
url: url,
dataType: 'text',
success: function(svgText) {
// 修复 id 冲突的问题
guid++;
svgText = String(svgText).replace(/SVGID_(\d+)_/g, function(match, $1) {
return 'SVG_GUID_' + guid + '_' + $1 + '_';
});
var parse = new DOMParser();
var svg = parse.parseFromString(svgText, 'text/xml');
var root = svg.querySelector('svg');
var each = [].forEach;
var viewbox = root.getAttribute('viewBox').split(' ');
var viewOffsetX = +viewbox[0];
var viewOffsetY = +viewbox[1];
var viewWidth = +viewbox[2];
var viewHeight = +viewbox[3];
var viewRatio = viewWidth / viewHeight;
var containerWidth = container.clientWidth;
var containerHeight = container.clientHeight;
var containerRatio = containerWidth / containerHeight;
var expand = 0;
var targetWidth, targetHeight, scale, xOffset, yOffset;
if (viewRatio > containerRatio) {
targetWidth = containerWidth;
targetHeight = targetWidth / viewRatio;
scale = targetWidth / viewWidth;
yOffset = (containerHeight - targetHeight) / 2;
xOffset = 0;
} else {
targetHeight = containerHeight;
targetWidth = containerHeight * viewRatio;
scale = targetHeight / viewHeight;
xOffset = (containerWidth - targetWidth) / 2;
yOffset = 0;
}
var units = [];
function transform(svgNode, divNode) {
each.call(svgNode.childNodes, function(g) {
if (!g.tagName || g.tagName.toLowerCase() != 'g' || !g.id) return;
var child = document.createElement('div');
if (g.id && g.id.indexOf(UNIT_PERFIX) === 0) {
child.id = g.id.substr('u-'.length);
child.classList.add('unit');
if (processor) {
processor(child);
}
delete g.id;
var unitContent = root.cloneNode(false);
unitContent.appendChild(g.cloneNode(true));
child.appendChild(unitContent);
units.push(child);
} else {
child.id = g.id;
transform(g, child);
}
divNode.appendChild(child);
});
}
transform(root, container);
units.forEach(function(unit) {
var unitContent = unit.querySelector('svg');
if (unitContent) {
var box = unitContent.querySelector('g').getBBox();
box = {
x: box.x - expand,
y: box.y - expand,
width: box.width + expand * 2,
height: box.height + expand * 2
};
var width = fixNumber(box.width * scale);
var height = fixNumber(box.height * scale);
if (width % 2) width++;
if (height % 2) height++;
unitContent.setAttribute('viewBox',
[box.x, box.y, box.width, box.height].map(fixNumber).join(' '));
unitContent.setAttribute('width', width);
unitContent.setAttribute('height', height);
unit.style.left = Math.round((box.x + viewOffsetX) * scale + xOffset) + 'px';
unit.style.top = Math.round((box.y + viewOffsetY) * scale + yOffset) + 'px';
unit.style.width = width + 'px';
unit.style.height = height + 'px';
if (/export_png/.test(window.location.href)) {
svg2png(unitContent, function(url) {
var a = document.createElement('a');
a.download = unit.id + '.png';
a.href = url;
var e = new MouseEvent('click');
a.dispatchEvent(e);
});
};
}
});
if (/export_html/.test(window.location.href)) {
units.forEach(function(unit) {
var left = Math.ceil(parseInt(unit.style.left) / 2) + 'px';
var top = Math.ceil(parseInt(unit.style.top) / 2) + 'px';
var width = Math.ceil(parseInt(unit.style.width) / 2) + 'px';
var height = Math.ceil(parseInt(unit.style.height) / 2) + 'px';
var backgroundImage = 'url(img/' + unit.id + '.png)';
unit.setAttribute('style', [
'left: ' + left,
'top: ' + top,
'width: ' + width,
'height: ' + height,
'background-image: ' + backgroundImage,
'background-size: ' + width + ' ' + height
].join('; '));
unit.innerHTML = '';
});
console.log(container.innerHTML);
}
resolve(units);
},
error: reject
});
});
}
exports.load = load;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment