11/**
22 * Svg loading plugin.
33 *
4- * This plugins loads SVG files and merges them into one SVG sprite
4+ * This plugin loads an svg graphic and defines it in the DOM, so you can reference it in a `<use>` tag.
55 *
66 * @example :
77 * To load the svg file `myicons/myicon.svg`:
88 * ```
9- * require(["requirejs-dplugins/svg!myicons/myicon.svg"], function (){
10- * // myicon was added to the sprite
9+ * require(["requirejs-dplugins/svg!myicons/myicon.svg"], function (myiconId ){
10+ * // myicon was added to the DOM
1111 * });
1212 * ```
1313 * @module requirejs-dplugins/svg
1616define ( [
1717 "./has" ,
1818 "./Promise!" ,
19- "module"
20- ] , function ( has , Promise , module ) {
19+ "module" ,
20+ "require" ,
21+ "requirejs-text/text" ,
22+ "requirejs-domready/domReady"
23+ ] , function ( has , Promise , module , localRequire , textPlugin ) {
2124 "use strict" ;
2225
23- var cache = { } , // paths of loaded svgs
26+ var loaded = { } , // paths of loaded svgs
2427 SPRITE_ID = 'requirejs-dplugins-svg' ,
2528 sprite = null ;
2629
@@ -31,47 +34,43 @@ define([
3134 /**
3235 * Loads an svg file.
3336 * @param {string } path - The svg file to load.
34- * @param {Function } require - A local require function to use to load other modules .
37+ * @param {Function } resourceRequire - A require function local to the module calling the svg plugin .
3538 * @param {Function } onload - A function to call when the specified svg file have been loaded.
3639 * @method
3740 */
38- load : function ( path , require , onload ) {
41+ load : function ( path , resourceRequire , onload ) {
3942 if ( has ( "builder" ) ) { // when building
40- cache [ path ] = true ;
43+ loaded [ path ] = true ;
4144 onload ( ) ;
4245 } else { // when running
43-
4446 // special case: when running a built version
4547 // Replace graphic by corresponding sprite.
48+ var idInLayer ;
4649 var layersMap = module . config ( ) . layersMap ;
47- if ( layersMap ) {
48- path = layersMap [ path ] || path ;
50+ if ( layersMap && layersMap [ path ] ) {
51+ idInLayer = layersMap [ path ] . id ;
52+ path = layersMap [ path ] . redirectTo ;
4953 }
5054
51- var filename = getFilename ( path ) ;
52- if ( path in cache ) {
53- cache [ path ] . then ( function ( graphic ) {
54- onload ( graphic . id ) ;
55- } ) ;
56- } else {
57- if ( ! sprite ) {
58- sprite = createSprite ( document , SPRITE_ID ) ;
59- document . body . appendChild ( sprite ) ;
60- }
61- cache [ path ] = new Promise ( function ( resolve ) {
62- require ( [ 'requirejs-text/text!' + path ] , function ( svgText ) {
63- var graphic = extractGraphic ( document , svgText , filename ) ,
64- symbol = createSymbol ( document , graphic . id , graphic . element , graphic . viewBox ) ;
65- sprite . appendChild ( symbol ) ;
66- cache [ path ] = graphic . id ;
67- resolve ( graphic ) ;
55+ if ( ! ( path in loaded ) ) {
56+ loaded [ path ] = new Promise ( function ( resolve ) {
57+ localRequire ( [ "requirejs-domready/domReady!" ] , function ( ) {
58+ textPlugin . load ( path , resourceRequire , function ( svgText ) {
59+ if ( ! sprite ) {
60+ sprite = createSprite ( document , SPRITE_ID ) ;
61+ document . body . appendChild ( sprite ) ;
62+ }
63+ var symbol = extractGraphicAsSymbol ( document , svgText ) ;
64+ sprite . appendChild ( symbol ) ;
65+ resolve ( symbol . getAttribute ( "id" ) ) ;
66+ } ) ;
6867 } ) ;
6968 } ) ;
70-
71- cache [ path ] . then ( function ( graphic ) {
72- onload ( graphic . id ) ;
73- } ) ;
7469 }
70+
71+ loaded [ path ] . then ( function ( symbolId ) {
72+ onload ( idInLayer || symbolId ) ;
73+ } ) ;
7574 }
7675 }
7776 } ;
@@ -89,8 +88,8 @@ define([
8988 * config: {
9089 * "requirejs-dplugins/svg": {
9190 * layersMap: {
92- * "file1.svg": "path/to/layer.svg",
93- * "file2.svg": "path/to/layer.svg"
91+ * "file1.svg": {redirectTo: "path/to/layer.svg", id: "id-inside-file-1"} ,
92+ * "file2.svg": {redirectTo: "path/to/layer.svg", id: "id-inside-file-2"}
9493 * }
9594 * }
9695 * }
@@ -101,19 +100,19 @@ define([
101100 * and writes it to the modules layer.
102101 * @param {string } mid - Current module id.
103102 * @param {string } dest - Current svg sprite path.
104- * @param {Array } loadList - List of svg files contained in current sprite.
103+ * @param {Array } loaded - Maps the paths of the svg files contained in current sprite to their ids .
105104 */
106- writeConfig : function ( write , mid , destMid , loadList ) {
105+ writeConfig : function ( write , mid , destMid , loaded ) {
107106 var svgConf = {
108107 config : { } ,
109108 paths : { }
110109 } ;
111110 svgConf . config [ mid ] = {
112111 layersMap : { }
113112 } ;
114- loadList . forEach ( function ( path ) {
115- svgConf . config [ mid ] . layersMap [ path ] = destMid ;
116- } ) ;
113+ for ( var path in loaded ) {
114+ svgConf . config [ mid ] . layersMap [ path ] = { redirectTo : destMid , id : loaded [ path ] } ;
115+ }
117116
118117 write ( "require.config(" + JSON . stringify ( svgConf ) + ");" ) ;
119118 } ,
@@ -124,84 +123,101 @@ define([
124123 * @param {Function } writePluginFiles - The write function provided by the builder to `writeFile`.
125124 * and writes it to the modules layer.
126125 * @param {string } dest - Current svg sprite path.
127- * @param {Array } loadList - List of svg files contained in current sprite.
126+ * @param {Array } loaded - Maps the paths of the svg files contained in current sprite to their ids .
128127 */
129- writeLayer : function ( writePluginFiles , dest , loadList ) {
130- var fs = require . nodeRequire ( "fs" ) ,
131- jsdom = require . nodeRequire ( "jsdom" ) . jsdom ;
132-
133- var document = jsdom ( "<html></html>" ) . parentWindow . document ;
134- var sprite = createSprite ( document ) ;
128+ writeLayer : function ( writePluginFiles , dest , loaded ) {
129+ function tryRequire ( paths ) {
130+ var module ;
131+ var path = paths . shift ( ) ;
132+ if ( path ) {
133+ try {
134+ // This is a node-require so it is synchronous.
135+ module = require . nodeRequire ( path ) ;
136+ } catch ( e ) {
137+ if ( e . code === "MODULE_NOT_FOUND" ) {
138+ return tryRequire ( paths ) ;
139+ } else {
140+ throw e ;
141+ }
142+ }
143+ }
144+ return module ;
145+ }
135146
136- loadList . forEach ( function ( path ) {
137- var filename = getFilename ( path ) ,
138- svgText = fs . readFileSync ( require . toUrl ( path ) , "utf8" ) ,
139- graphic = extractGraphic ( document , svgText , filename ) ,
140- symbol = createSymbol ( document , graphic . id , graphic . element , graphic . viewBox ) ;
141- sprite . appendChild ( symbol ) ;
142- } ) ;
147+ var fs = require . nodeRequire ( "fs" ) ,
148+ jsDomPath = require . getNodePath ( require . toUrl ( module . id ) . replace ( / [ ^ \/ ] * $ / , "node_modules/jsdom" ) ) ,
149+ jsDomModule = tryRequire ( [ jsDomPath , "jsdom" ] ) ;
150+
151+ var path , url , svgText ;
152+ if ( ! jsDomModule ) {
153+ console . log ( ">> WARNING: Node module jsdom not found. Skipping SVG bundling. If you" +
154+ " want SVG bundling run 'npm install jsdom' in your console." ) ;
155+ for ( path in loaded ) {
156+ url = require . toUrl ( path ) ;
157+ svgText = fs . readFileSync ( url , "utf8" ) ;
158+ writePluginFiles ( url , svgText ) ;
159+ }
160+ return false ;
161+ } else {
162+ var jsdom = jsDomModule . jsdom ,
163+ document = jsdom ( "<html></html>" ) ,
164+ sprite = createSprite ( document ) ;
165+
166+ for ( path in loaded ) {
167+ url = require . toUrl ( path ) ;
168+ svgText = fs . readFileSync ( url , "utf8" ) ;
169+ var symbol = extractGraphicAsSymbol ( document , svgText ) ;
170+ sprite . appendChild ( symbol ) ;
171+ loaded [ path ] = symbol . getAttribute ( "id" ) ;
172+ }
143173
144- writePluginFiles ( dest , sprite . outerHTML ) ;
174+ writePluginFiles ( dest , sprite . outerHTML ) ;
175+ return true ;
176+ }
145177 }
146178 } ;
147179
148-
149180 loadSVG . writeFile = function ( pluginName , resource , require , write ) {
150181 writePluginFiles = write ;
151182 } ;
152183
153- loadSVG . addModules = function ( pluginName , resource , addModules ) {
154- addModules ( [ "requirejs-text/text" ] ) ;
155- } ;
156-
157184 loadSVG . onLayerEnd = function ( write , layer ) {
158185 if ( layer . name && layer . path ) {
159186 var dest = layer . path . replace ( / ^ ( .* \/ ) ? ( .* ) .j s $ / , "$1/$2.svg" ) ,
160187 destMid = layer . name + ".svg" ;
161188
162- var loadList = Object . keys ( cache ) ;
163-
164189 // Write layer file and config
165- buildFunctions . writeLayer ( writePluginFiles , dest , loadList ) ;
166- buildFunctions . writeConfig ( write , module . id , destMid , loadList ) ;
190+ var success = buildFunctions . writeLayer ( writePluginFiles , dest , loaded ) ;
191+ success && buildFunctions . writeConfig ( write , module . id , destMid , loaded ) ;
167192
168193 // Reset cache
169- cache = { } ;
194+ loaded = { } ;
170195 }
171196 } ;
172197
173198 }
174199
175200 return loadSVG ;
176201
177-
178- // takes a path and returns the filename
179- function getFilename ( filepath ) {
180- return filepath . replace ( / .* \/ ( .* ) \. s v g $ / , "$1" ) ;
181- }
182-
183202 // makes a symbol out of an svg graphic
184- function extractGraphic ( document , svgText , filename ) {
203+ function extractGraphicAsSymbol ( document , svgText ) {
185204 var div = document . createElement ( "div" ) ;
186205 div . innerHTML = svgText ;
187206 var element = div . querySelector ( "svg" ) ,
188- id = element . getAttribute ( "id" ) || filename ,
189- viewBox = element . getAttribute ( "viewbox" ) || element . getAttribute ( "viewBox" ) || "" ;
190- return {
191- id : id ,
192- viewBox : viewBox ,
193- element : element
194- } ;
207+ id = element . getAttribute ( "id" ) ,
208+ viewBox = element . getAttribute ( "viewbox" ) || element . getAttribute ( "viewBox" ) ,
209+ symbol = createSymbol ( document , id , element , viewBox ) ;
210+ return symbol ;
195211 }
196212
197213 // makes symbol from svg element
198214 function createSymbol ( document , id , element , viewBox ) {
199215 var symbol = document . createElementNS ( "http://www.w3.org/2000/svg" , "symbol" ) ;
200- symbol . setAttribute ( "id" , id ) ;
201216 while ( element . firstChild ) {
202217 symbol . appendChild ( element . firstChild ) ;
203218 }
204- viewBox && symbol . setAttribute ( "viewBox" , viewBox ) ;
219+ typeof id === "string" && symbol . setAttribute ( "id" , id ) ;
220+ typeof viewBox === "string" && symbol . setAttribute ( "viewBox" , viewBox ) ;
205221 return symbol ;
206222 }
207223
0 commit comments