Skip to content

Instantly share code, notes, and snippets.

@antimatter15
Created January 4, 2010 21:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save antimatter15/268884 to your computer and use it in GitHub Desktop.
Save antimatter15/268884 to your computer and use it in GitHub Desktop.
//this file hacked to be svg-edit only and work with JSONP
/*
* SVG Icon Loader 2.0
*
* jQuery Plugin for loading SVG icons from a single file
*
* Copyright (c) 2009 Alexis Deveria
* http://a.deveria.com
*
* Apache 2 License
*/
(function($) {
var svg_icons = {};
$.svgIcons = function(file, opts) {
var svgns = "http://www.w3.org/2000/svg",
xlinkns = "http://www.w3.org/1999/xlink",
icon_w = opts.w?opts.w : 24,
icon_h = opts.h?opts.h : 24,
elems, svgdoc, testImg,
icons_made = false, data_loaded = false, load_attempts = 0,
ua = navigator.userAgent, isOpera = !!window.opera, isSafari = (ua.indexOf('Safari/') > -1 && ua.indexOf('Chrome/')==-1),
data_pre = 'data:image/svg+xml;charset=utf-8;base64,';
if(opts.svgz) {
var data_el = $('<object data="' + file + '" type=image/svg+xml>').appendTo('body').hide();
try {
svgdoc = data_el[0].contentDocument;
// TODO: IE still loads this, shouldn't even bother.
data_el.load(getIcons);
getIcons(0, true); // Opera will not run "load" event if file is already cached
} catch(err1) {
useFallback();
}
} else {
$.ajax({
url: "http://anti15.welfarehost.com/svgeditproxy.php?whoot=?",
dataType: 'jsonp',
success: function(data) {
if (window.DOMParser)
{
parser=new DOMParser();
xmlDoc=parser.parseFromString(data,"text/xml");
}
else // Internet Explorer
{
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async="false";
xmlDoc.loadXML(data);
}
svgdoc = xmlDoc;
$(function() {
getIcons('ajax');
});
},
error: function() {
$(function() {
useFallback();
});
}
});
}
function getIcons(evt, no_wait) {
if(evt !== 'ajax') {
if(data_loaded) return;
// Webkit sometimes says svgdoc is undefined, other times
// it fails to load all nodes. Thus we must make sure the "eof"
// element is loaded.
svgdoc = data_el[0].contentDocument; // Needed again for Webkit
var isReady = (svgdoc && svgdoc.getElementById('svg_eof'));
if(!isReady && !(no_wait && isReady)) {
load_attempts++;
if(load_attempts < 50) {
setTimeout(getIcons, 20);
} else {
useFallback();
data_loaded = true;
}
return;
}
data_loaded = true;
}
// Clean source SVGs (mostly for Inkscape files)
// TODO: Find a way to do this without crashing Safari (when converting to IMG)
$(svgdoc).find('metadata').remove().end()
.find('*').each(function(i, el) {
if(el.nodeName.indexOf(':') != -1) {
$(el).remove();
}
var attrs = $.extend(false, el.attributes, {});
for(i in attrs) {
var attr = attrs[i];
var fullattr = attr.prefix?attr.prefix + ':' + attr.localName:'';
if(attr.prefix) {
el.removeAttribute(attr.localName); // for Opera
el.removeAttribute(fullattr); // for Webkit
}
if(fullattr == 'xlink:href') {
el.setAttribute('xlink:href', attr.nodeValue);
}
}
});
elems = $(svgdoc.firstChild).children(); //.getElementsByTagName('foreignContent');
var testSrc = data_pre + 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNzUiIGhlaWdodD0iMjc1Ij48L3N2Zz4%3D';
testImg = $(new Image()).attr({
src: testSrc,
width: 0,
height: 0
}).appendTo('body')
.load(function () {
// Safari 4 crashes, Opera and Chrome don't
makeIcons(!isSafari);
}).error(function () {
makeIcons();
});
}
function makeIcons(toImage, fallback) {
if(icons_made) return;
if(opts.no_img) toImage = false;
var holder;
var setIcon = function(target, icon, id, setID) {
if(isOpera) icon.css('visibility','hidden');
if(opts.replace) {
if(setID) icon.attr('id',id);
var cl = target.attr('class');
if(cl) icon.attr('class','svg_icon '+cl);
target.replaceWith(icon);
} else {
target.append(icon);
}
if(isOpera) {
setTimeout(function() {
icon.attr('style','visibility:visible;');
},1);
}
}
var addIcon = function(icon, id) {
if(opts.id_match === undefined || opts.id_match !== false) {
setIcon(holder, icon, id, true);
}
svg_icons[id] = icon;
}
if(toImage) {
var temp_holder = $(document.createElement('div'));
temp_holder.hide().appendTo('body');
}
if(fallback) {
var path = opts.fallback_path?opts.fallback_path:'';
$.each(fallback, function(id, imgsrc) {
holder = $('#' + id);
var icon = $(new Image())
.attr({
'class':'svg_icon',
src: path + imgsrc,
'width': icon_w,
'height': icon_h,
'alt': 'icon'
});
addIcon(icon, id);
});
} else {
$.each(elems, function(i, elem) {
var id = elem.getAttribute('id');
if(id == 'svg_eof') return;
holder = $('#' + id);
var svg = elem.getElementsByTagNameNS(svgns, 'svg')[0];
var svgroot = svgdoc.createElementNS(svgns, "svg");
svgroot.setAttributeNS(svgns, 'viewBox', [0,0,icon_w,icon_h].join(' '));
$(svgroot).attr({
"xmlns": svgns,
"width": icon_w,
"height": icon_h,
"xmlns:xlink": xlinkns,
"class": 'svg_icon'
});
// Without cloning, Firefox will make another GET request.
// With cloning, causes issue in Opera/Win/Non-EN
if(!isOpera) svg = svg.cloneNode(true);
svgroot.appendChild(svg);
if(toImage) {
// Without cloning, Safari will crash
// With cloning, causes issue in Opera/Win/Non-EN
var svgcontent = isOpera?svgroot:svgroot.cloneNode(true);
temp_holder.empty().append(svgroot);
var str = data_pre + encode64(temp_holder.html());
var icon = $(new Image())
.attr({'class':'svg_icon', src:str});
} else {
var icon = fixIDs($(svgroot), i);
}
addIcon(icon, id);
});
}
if(opts.placement) {
$.each(opts.placement, function(sel, id) {
if(!svg_icons[id]) return;
$(sel).each(function(i) {
var copy = svg_icons[id].clone();
if(i > 0 && !toImage) copy = fixIDs(copy, i, true);
setIcon($(this), copy, id);
})
});
}
if(!fallback) {
if(toImage) temp_holder.remove();
if(data_el) data_el.remove();
testImg.remove();
}
if(opts.resize) $.resizeSvgIcons(opts.resize);
icons_made = true;
if(opts.callback) opts.callback(svg_icons);
}
function fixIDs(svg_el, svg_num, force) {
var defs = svg_el.find('defs');
if(!defs.length) return svg_el;
defs.find('[id]').each(function(i) {
var id = this.id;
var no_dupes = ($(svgdoc).find('#' + id).length <= 1);
if(isOpera) no_dupes = false; // Opera didn't clone svg_el, so not reliable
if(!force && no_dupes) return;
var new_id = id + svg_num + i;
$(this).attr('id', new_id);
svg_el.find('[fill="url(#' + id + ')"]').each(function() {
$(this).attr('fill', 'url(#' + new_id + ')');
}).end().find('[stroke="url(#' + id + ')"]').each(function() {
$(this).attr('stroke', 'url(#' + new_id + ')');
}).end().find('use').each(function() {
if(this.getAttribute('xlink:href') == '#' + id) {
this.setAttributeNS(xlinkns,'href','#' + new_id);
}
});
});
return svg_el;
}
function useFallback() {
if(file.indexOf('.svgz') != -1) {
var reg_file = file.replace('.svgz','.svg');
if(window.console) {
console.log('.svgz failed, trying with .svg');
}
$.svgIcons(reg_file, opts);
} else if(opts.fallback) {
makeIcons(false, opts.fallback);
}
}
function encode64(input) {
// base64 strings are 4/3 larger than the original string
if(window.btoa) return window.btoa(input);
var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var output = new Array( Math.floor( (input.length + 2) / 3 ) * 4 );
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0, p = 0;
do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output[p++] = _keyStr.charAt(enc1);
output[p++] = _keyStr.charAt(enc2);
output[p++] = _keyStr.charAt(enc3);
output[p++] = _keyStr.charAt(enc4);
} while (i < input.length);
return output.join('');
}
}
$.getSvgIcon = function(id) { return svg_icons[id]; }
$.resizeSvgIcons = function(obj) {
// FF2 and older don't detect .svg_icon, so we change it detect svg elems instead
var change_sel = !$('.svg_icon:first').length;
$.each(obj, function(sel, size) {
var arr = $.isArray(size);
var w = arr?size[0]:size,
h = arr?size[1]:size;
if(change_sel) {
sel = sel.replace(/\.svg_icon/g,'svg');
}
$(sel).each(function() {
this.setAttribute('width', w);
this.setAttribute('height', h);
});
});
}
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment