debugger - ¿Hay alguna forma de "cambiar en caliente" el código JavaScript dentro del navegador?
javascript debugger chrome (6)
¿Hay alguna herramienta que le permita "intercambiar en caliente" los contenidos de JavaScript mientras se ejecuta una página web?
Estoy buscando algo similar a lo que hace HotSpot para Java, una forma de "implementar en caliente" el nuevo código JS sin tener que volver a cargar toda la página.
¿Hay algo así por ahí?
Aclaración en caso de que las personas no entiendan "intercambio en caliente", como lo indica el bloqueo :
Con "intercambio en caliente" me refiero a permitirme cambiar partes del código contenido en la página y sus archivos .js.
Entonces este marco detectaría el cambio, ya sea automágicamente o por alguna indicación de mi parte, y volvería a cargar el código de forma dinámica, evitando la nueva publicación del lado del servidor (recargar).
Ese tipo de enfoque simplificaría la eliminación de errores y la corrección de errores, ya que no necesita volver a cargar la página y comenzar la interacción desde el principio.
Idea interesante :)
Escribí el siguiente bookmarklet:
function reload(){var scripts=document.getElementsByTagName("script");var head=document.getElementsByTagName("head")[0];var newScripts=[];var removeScripts=[];for(var i=0;i<scripts.length;i++){var parent=scripts[i].parentNode;if(parent==head&&scripts[i].src){var newScript={};newScript.src=scripts[i].src;newScript.innerHTML=scripts[i].innerHTML;newScripts.push(newScript);removeScripts.push(scripts[i]);}}for(var i=0;i<removeScripts.length;i++){head.removeChild(removeScripts[i]);}for(var i=0;i<newScripts.length;i++){var script=document.createElement("script");script.src=newScripts[i].src;script.type="text/javascript";script.innerHTML=newScripts[i].innerHTML;head.appendChild(script);}}
agréguelo a la ubicación de un nuevo marcador, y volverá a cargar todos los javascripts a los que se hace referencia en <head>. No estoy seguro de qué tan bien funcionará en la práctica, pero valió la pena intentarlo :) Supongo que tendrías que tener mucho cuidado en la forma en que escribes tus guiones, para no tener que agregar cosas al cuerpo de la página varias veces, etc. Tal vez el soporte para un atributo ''reload = "true'' ''podría ser útil, de esa manera podría etiquetar solo sus bibliotecas como recargables.
Fuente completa:
function reload() {
var scripts = document.getElementsByTagName("script");
var head = document.getElementsByTagName("head")[0];
var newScripts = [];
var removeScripts = [];
for(var i=0; i < scripts.length; i++) {
var parent = scripts[i].parentNode;
if(parent == head && scripts[i].src) {
var newScript = {};
newScript.src = scripts[i].src;
newScript.innerHTML = scripts[i].innerHTML;
newScripts.push(newScript);
removeScripts.push(scripts[i]);
}
}
for(var i=0; i < removeScripts.length; i++) {
head.removeChild(removeScripts[i]);
}
for(var i=0; i < newScripts.length; i++) {
var script = document.createElement("script");
script.src = newScripts[i].src;
script.type = "text/javascript";
script.innerHTML = newScripts[i].innerHTML;
head.appendChild(script);
}
}
No estoy familiarizado con HotSport, pero si habla de cargar dinámicamente JavaScript, sí, puede hacerlo. MooTools te permite hacer eso, al igual que jQuery , Prototype , Dojo y YUI , y estoy seguro de que la mayoría de los otros frameworks también lo hacen. También puedes hacerlo con JavaScript nativo .
estoy seguro de que no mucha gente sabe lo que quieres decir con hot swap, pero como virtuosiMedia dijo que mootools permite eso, si no confías mucho en mootools, entonces hay un mismo complemento para jquery y, si no quieres frameworks, siempre puedes agregar esos scripts a través de dom
pero estoy seguro de que no puedes alterar las secuencias de comandos de nivel inicial que ya has definido, ya que las secuencias de comandos añadidas dinámicamente a través de DOM son solo de nivel corporal
Como tenía un problema similar que resolver, escribí una pequeña js lib para hotswap javascript, css y archivos de imágenes. Por supuesto, es de código abierto en github: hotswap.js
Espero eso ayude.
Actualización : he adjuntado la fuente completa de lib aquí. Para usarlo, simplemente copie el contenido en un archivo (por ejemplo: hotswap.js) e inserte la etiqueta del script en su sitio web de esta manera:
<script src="hotswap.js"></script>
API:
// refresh .js files
hotswap.refreshAllJs(arrExcludedFiles);
hotswap.refreshJs(arrIncludedFiles);
// refresh .css files
hotswap.refreshAllCss(arrExcludedFiles);
hotswap.refreshCss(arrIncludedFiles);
// refresh images
hotswap.refreshAllImg(arrExcludedFiles);
hotswap.refreshImg(arrIncludedFiles);
// show a gui (this is optional and not required for hotswap to work) (Click on the "H").
hotswap.createGui();
// Examples:
// refresh all .js files
hotswap.refreshAllJs();
// refresh main.css only
hotswap.refreshCss( ["main.js"] );
// refresh all images (img tags) except "dont-refreh-me.png".
hotswap.refreshAllImg( ["dont-refreh-me.png"] );
Fuente completa (v 0.2.0):
Tuve que eliminar todos los comentarios para que quedaran por debajo del límite de respuesta de 30000 caracteres. El html + css en línea es feo, lo sé, pero quería mantenerlo dentro de un solo archivo .js.
(function() {
var root = this;
var previousHotswap = root.hotswap;
var hotswap = function()
{
if (!(this instanceof hotswap))
{
return new hotswap();
}
else
{
return this;
}
};
root.hotswap = hotswap();
hotswap.prototype.VERSION = ''0.2.0'';
hotswap.prototype.RND_PARAM_NAME = ''hs982345jkasg89zqnsl'';
hotswap.prototype.FILE_REMOVAL_DELAY = 400;
hotswap.prototype.CSS_HTML_PREFIX = ''hs982345jkasg89zqnsl'';
hotswap.prototype._prefix = false;
hotswap.prototype._prefixCache = [];
hotswap.prototype._guiCache = {};
hotswap.prototype._guiGuiRefreshInterval = null;
hotswap.prototype._guiHtml = '''' +
''<style type="text/css">''+
'' #PREFIX''+
'' {''+
'' display: block;''+
'' position: fixed;''+
'' top: 20%;/*distance from top*/''+
'' right: 0;''+
'' z-index: 99999;''+
'' width: 20em;''+
'' height: auto;''+
'' color: black;''+
'' background-color: #666666;''+
'' font-family: Verdana, sans-serif;''+
'' font-size: 0.8em;''+
'' -webkit-box-shadow: 0 0px 0.3em 0.1em #999999;''+
'' -moz-box-shadow: 0 0px 0.3em 0.1em #999999;''+
'' box-shadow: 0 0px 0.3em 0.1em #999999;''+
'' }''+
'' #PREFIX.mini''+
'' {''+
'' width: 2.9em;''+
'' height: 2.9em;''+
'' overflow:hidden;''+
'' }''+
'' #PREFIX.mini .PREFIX-header input, #PREFIX.mini .PREFIX-list, #PREFIX.mini .PREFIX-footer''+
'' {''+
'' display:none;''+
'' }''+
'' #PREFIX.mini .PREFIX-header div''+
'' {''+
'' display: block;''+
'' width: 100%;''+
'' height: 100%;''+
'' }''+
'' #PREFIX input''+
'' {''+
'' font-size: 1.0em;''+
'' border: 0.1em solid #999999;''+
'' border-radius: 0.2em;''+
'' padding: 0.2em 0.1em;''+
'' }''+
'' #PREFIX .PREFIX-header''+
'' {''+
'' height: 2.4em;''+
'' overflow:hidden;''+
'' padding: 0.4em;''+
'' color: white;''+
'' background-color: black;''+
'' }''+
'' #PREFIX .PREFIX-header input''+
'' {''+
'' width: 83.5%;''+
'' height: 1.6em;''+
'' }''+
'' #PREFIX .PREFIX-header div''+
'' {''+
'' position: absolute;''+
'' top:0;''+
'' right:0;''+
'' width: 14.5%;''+
'' height: 1.6em;''+
'' line-height: 1.4em;''+
'' text-align: center;''+
'' font-size: 2em;''+
'' font-weight: bold;''+
'' cursor: pointer;''+
'' }''+
'' #PREFIX .PREFIX-header div:hover''+
'' {''+
'' background-color: #444444;''+
'' }''+
'' #PREFIX .PREFIX-list''+
'' {''+
'' width: 100%;''+
'' height: 22em;''+
'' overflow: auto;''+
'' }''+
'' #PREFIX ul''+
'' {''+
'' list-style-type: none;''+
'' list-style-position: inside;''+
'' padding: 0;''+
'' margin: 0.5em 0.5em 1.2em 0.5em;''+
'' }''+
'' #PREFIX ul li''+
'' {''+
'' margin: 0.3em;''+
'' padding: 0.5em 0.5em;''+
'' color: white;''+
'' background-color: #717171;''+
'' font-size: 0.9em;''+
'' line-height: 1.5em;''+
'' cursor: pointer;''+
'' }''+
'' #PREFIX ul li:hover''+
'' {''+
'' background-color: #797979;''+
'' }''+
'' #PREFIX ul li.template''+
'' {''+
'' display: none;''+
'' }''+
'' #PREFIX ul li.active''+
'' {''+
'' background-color: black;''+
'' }''+
'' #PREFIX ul li.PREFIX-headline''+
'' {''+
'' color: white;''+
'' background-color: transparent;''+
'' text-align: center;''+
'' font-weight: bold;''+
'' cursor: default;''+
'' }''+
'' #PREFIX .PREFIX-footer''+
'' {''+
'' padding: 0;''+
'' margin:0;''+
'' background-color: #444444;''+
'' }''+
'' #PREFIX .PREFIX-footer ul''+
'' {''+
'' margin: 0;''+
'' padding: 0.5em;''+
'' }''+
'' #PREFIX .PREFIX-footer ul li''+
'' {''+
'' color: white;''+
'' background-color: black;''+
'' font-size: 1.0em;''+
'' border-radius: 0.5em;''+
'' text-align: center;''+
'' height: 2.2em;''+
'' line-height: 2.2em;''+
'' }''+
'' #PREFIX .PREFIX-footer ul li input.PREFIX-seconds''+
'' {''+
'' text-align: center;''+
'' width: 2em;''+
'' }''+
'' #PREFIX .PREFIX-footer ul li:hover''+
'' {''+
'' background-color: #222222;''+
'' }''+
'' #PREFIX .PREFIX-footer ul li.inactive''+
'' {''+
'' background-color: #666666;''+
'' cursor: default;''+
'' }''+
'' </style>''+
'' <div id="PREFIX" class="mini">''+
'' <div class="PREFIX-header">''+
'' <input id="PREFIX-prefix" placeholder="prefix" type="text" name="" />''+
'' <div id="PREFIX-toggle">H</div>''+
'' </div>''+
'' <div class="PREFIX-list">''+
'' <ul id="PREFIX-css">''+
'' <li class="PREFIX-headline">CSS</li>''+
'' <li class="template"></li>''+
'' </ul>''+
'' <ul id="PREFIX-js">''+
'' <li class="PREFIX-headline">JS</li>''+
'' <li class="template"></li>''+
'' </ul>''+
'' <ul id="PREFIX-img">''+
'' <li class="PREFIX-headline">IMG</li>''+
'' <li class="template"></li>''+
'' </ul>''+
'' </div>''+
'' <div class="PREFIX-footer">''+
'' <ul>''+
'' <li id="PREFIX-submit-selected">refresh selected</li>''+
'' <li id="PREFIX-submit-start">refresh every <input class="PREFIX-seconds" type="text" value="1" /> sec.</li>''+
'' <li id="PREFIX-submit-stop" class="inactive">stop refreshing</li>''+
'' <li id="PREFIX-submit-refresh-list">refresh list</li>''+
'' </ul>''+
'' </div>''+
'' </div>'';
var
xGetElementById = function(sId){ return document.getElementById(sId) },
xGetElementsByTagName = function(sTags){ return document.getElementsByTagName(sTags) },
xAppendChild = function(parent, child){ return parent.appendChild(child) },
xCloneNode = function(node){ return document.cloneNode(node) },
xCreateElement = function(sTag){ return document.createElement(sTag) },
xCloneNode = function(ele, deep){ return ele.cloneNode(deep) },
xRemove = function(ele)
{
if( typeof ele.parentNode != "undefined" && ele.parentNode )
{
ele.parentNode.removeChild( ele );
}
},
xAddEventListener = function(ele, sEvent, fn, bCaptureOrBubble)
{
if( xIsEmpty(bCaptureOrBubble) )
{
bCaptureOrBubble = false;
}
if (ele.addEventListener)
{
ele.addEventListener(sEvent, fn, bCaptureOrBubble);
return true;
}
else if (ele.attachEvent)
{
return ele.attachEvent(''on'' + sEvent, fn);
}
else
{
ele[''on'' + sEvent] = fn;
}
},
xStopPropagation = function(evt)
{
if (evt && evt.stopPropogation)
{
evt.stopPropogation();
}
else if (window.event && window.event.cancelBubble)
{
window.event.cancelBubble = true;
}
},
xPreventDefault = function(evt)
{
if (evt && evt.preventDefault)
{
evt.preventDefault();
}
else if (window.event && window.event.returnValue)
{
window.eventReturnValue = false;
}
},
xContains = function(sHaystack, sNeedle)
{
return sHaystack.indexOf(sNeedle) >= 0
},
xStartsWith = function(sHaystack, sNeedle)
{
return sHaystack.indexOf(sNeedle) === 0
},
xReplace = function(sHaystack, sNeedle, sReplacement)
{
if( xIsEmpty(sReplacement) )
{
sReplacement = "";
}
return sHaystack.split(sNeedle).join(sReplacement);
},
xGetAttribute = function(ele, sAttr)
{
var result = (ele.getAttribute && ele.getAttribute(sAttr)) || null;
if( !result ) {
result = ele[sAttr];
}
if( !result ) {
var attrs = ele.attributes;
var length = attrs.length;
for(var i = 0; i < length; i++)
if(attrs[i].nodeName === sAttr)
result = attrs[i].nodeValue;
}
return result;
},
xSetAttribute = function(ele, sAttr, value)
{
if(ele.setAttribute)
{
ele.setAttribute(sAttr, value)
}
else
{
ele[sAttr] = value;
}
},
xGetParent = function(ele)
{
return ele.parentNode || ele.parentElement;
},
xInsertAfter = function( refEle, newEle )
{
return xGetParent(refEle).insertBefore(newEle, refEle.nextSibling);
},
xBind = function(func, context)
{
if (Function.prototype.bind && func.bind === Function.prototype.bind)
{
return func.bind(context);
}
else
{
return function() {
if( arguments.length > 2 )
{
return func.apply(context, arguments.slice(2));
}
else
{
return func.apply(context);
}
};
}
},
xIsEmpty = function(value)
{
var ret = true;
if( value instanceof Object )
{
for(var i in value){ if(value.hasOwnProperty(i)){return false}}
return true;
}
ret = typeof value === "undefined" || value === undefined || value === null || value === "";
return ret;
},
xAddClass = function(ele, sClass)
{
var clazz = xGetAttribute( ele, "class" );
if( !xHasClass(ele, sClass) )
{
xSetAttribute( ele, "class", clazz + " " + sClass );
}
},
xRemoveClass = function(ele, sClass)
{
var clazz = xGetAttribute( ele, "class" );
if( xHasClass(ele, sClass) )
{
xSetAttribute( ele, "class", xReplace( clazz, sClass, "" ) );
}
},
xHasClass = function(ele, sClass)
{
var clazz = xGetAttribute( ele, "class" );
return !xIsEmpty(clazz) && xContains( clazz, sClass );
};
hotswap.prototype._recreate = function( type, xcludedFiles, xcludeComparator, nDeleteDelay, bForceRecreation )
{
if( typeof nDeleteDelay == "undefined")
{
nDeleteDelay = 0;
}
if( typeof bForceRecreation == "undefined")
{
bForceRecreation = false;
}
var tags = this._getFilesByType(type, xcludedFiles, xcludeComparator);
var newTags = [];
var removeTags = [];
var i, src, detected, node, srcAttributeName;
for(i=0; i<tags.length; i++)
{
node = tags[i].node;
srcAttributeName = tags[i].srcAttributeName;
var newNode = {
node: null,
oldNode: node,
parent: xGetParent(node)
};
if( bForceRecreation )
{
newNode.node = xCreateElement("script");
}
else
{
newNode.node = xCloneNode(node, false);
}
for (var p in node) {
if (node.hasOwnProperty(p)) {
newNode.node.p = node.p;
}
}
src = xGetAttribute( node, srcAttributeName );
xSetAttribute( newNode.node, srcAttributeName, this._updatedUrl(src) );
newTags.push(newNode);
removeTags.push(node);
}
for(var i=0; i < newTags.length; i++) {
xInsertAfter(newTags[i].oldNode, newTags[i].node);
}
if( nDeleteDelay > 0 )
{
for(var i=0; i < removeTags.length; i++) {
xSetAttribute(removeTags[i], "data-hotswap-deleted", "1");
}
setTimeout( function() {
for(var i=0; i < removeTags.length; i++) {
xRemove(removeTags[i]);
}
}, nDeleteDelay);
}
else
{
for(var i=0; i < removeTags.length; i++) {
xRemove(removeTags[i]);
}
}
};
hotswap.prototype._reload = function( type, xcludedFiles, xcludeComparator )
{
var tags = this._getFilesByType(type, xcludedFiles, xcludeComparator);
var i, src, node, srcAttributeName;
for(i=0; i<tags.length; i++)
{
node = tags[i].node;
srcAttributeName = tags[i].srcAttributeName;
// update the src property
src = xGetAttribute( node, srcAttributeName );
xSetAttribute( node, srcAttributeName, this._updatedUrl(src) );
}
};
hotswap.prototype._getFilesByType = function( type, xcludedFiles, xcludeComparator )
{
var files;
switch(type)
{
case "css":
files = this._getFiles(
"css",
"link",
function(ele)
{
return (xGetAttribute(ele, "rel") == "stylesheet" || xGetAttribute(ele, "type") == "text/css");
},
"href",
xcludedFiles,
xcludeComparator
)
break;
case "js":
files = this._getFiles(
"js",
"script",
function(ele)
{
return (xGetAttribute(ele, "type") == "" || xGetAttribute(ele, "type") == "text/javascript");
},
"src",
xcludedFiles,
xcludeComparator
)
break;
case "img":
files = this._getFiles(
"img",
"img",
function(ele)
{
return (xGetAttribute(ele, "src") != "");
},
"src",
xcludedFiles,
xcludeComparator
)
break;
}
return files;
}
hotswap.prototype._getFiles = function( type, tagName, tagFilterFunc, srcAttributeName, xcludedFiles, xcludeComparator )
{
if( typeof xcludedFiles == "undefined" || !xcludedFiles)
{
xcludedFiles = [];
}
if( typeof xcludeComparator == "undefined" || !xcludeComparator)
{
xcludeComparator = false;
}
var fileNodes = [];
var tags = xGetElementsByTagName(tagName);
var src, detected, node;
for(var i=0; i<tags.length; i++) {
node = tags[i];
src = xGetAttribute(node,[srcAttributeName]);
if( xIsEmpty( xGetAttribute(node, "data-hotswap-deleted") ) )
{
if(src && tagFilterFunc(node))
{
detected = false;
for(var j=0; j<xcludedFiles.length; j++) {
if( xContains(src,xcludedFiles[j]) )
{
detected = true;
break;
}
}
if( detected == xcludeComparator )
{
fileNodes.push({
type: type,
node : node,
tagName : tagName,
srcAttributeName : srcAttributeName
});
}
}
}
}
return fileNodes;
};
hotswap.prototype._updatedUrl = function( url, getCleanUrl )
{
var cleanUrl;
if( typeof getCleanUrl == "undefined")
{
getCleanUrl = false;
}
url = cleanUrl = url.replace(new RegExp("(//?|&)"+this.RND_PARAM_NAME+"=[0-9.]*","g"), "");
var queryString = "", randomizedQueryString = "";
if( xContains(url, "?") )
{
if(xContains(url, "&" + this.RND_PARAM_NAME))
{
queryString = url.split("&" + this.RND_PARAM_NAME).slice(1,-1).join("");
}
randomizedQueryString = queryString + "&" + this.RND_PARAM_NAME + "=" + Math.random() * 99999999;
}
else
{
if(xContains(url, "?" + this.RND_PARAM_NAME))
{
queryString = url.split("?" + this.RND_PARAM_NAME).slice(1,-1).join("");
}
randomizedQueryString = queryString + "?" + this.RND_PARAM_NAME + "=" + Math.random() * 99999999;
}
var foundAt = -1;
if( !xIsEmpty( this._prefixCache ) )
{
for(var i=0; i<this._prefixCache.length; ++i)
{
if( !xIsEmpty(this._prefixCache[i]) && foundAt < 0 )
{
for(var h=0; h<this._prefixCache[i].length; ++h)
{
if( this._prefixCache[i][h] == cleanUrl + queryString )
{
cleanUrl = this._prefixCache[i][0];
foundAt = i;
break;
}
}
}
}
}
var prefixHistory = [cleanUrl + queryString];
var applyPrefix = true;
if( prefixHistory[0].match( new RegExp(''^[A-Za-z0-9-_]+://'') ) )
{
applyPrefix = false;
}
var prefix = this._prefix;
if( !xIsEmpty(this._prefix) && this._prefix )
{
prefixHistory.push( this._prefix + cleanUrl + queryString );
if(foundAt >= 0)
{
this._prefixCache[foundAt] = prefixHistory;
}
else
{
this._prefixCache.push( prefixHistory );
}
}
else
{
prefix = "";
}
if( !applyPrefix )
{
prefix = "";
}
url = prefix + cleanUrl + randomizedQueryString;
return (getCleanUrl) ? (cleanUrl + queryString) : url;
}
hotswap.prototype.refreshAllJs = function( excludedFiles )
{
if( typeof excludedFiles == "undefined" || !excludedFiles)
{
excludedFiles = []
}
excludedFiles.push("hotswap.js");
this._recreate( "js", excludedFiles, false, 0, true );
};
hotswap.prototype.refreshJs = function( includedFiles )
{
this._recreate( "js", includedFiles, true, 0, true );
};
hotswap.prototype.refreshAllCss = function( excludedFiles )
{
this._recreate( "css", excludedFiles, false, this.FILE_REMOVAL_DELAY );
};
hotswap.prototype.refreshCss = function( includedFiles )
{
this._recreate( "css", includedFiles, true, this.FILE_REMOVAL_DELAY );
};
hotswap.prototype.refreshAllImg = function( excludedFiles )
{
this._reload( "img", excludedFiles, false );
};
hotswap.prototype.refreshImg = function( includedFiles )
{
this._reload( "img", includedFiles, true );
};
hotswap.prototype.setPrefix = function( prefix )
{
this._prefix = prefix;
var gui = xGetElementById(this.CSS_HTML_PREFIX + "_wrapper");
if( gui )
{
if( !xIsEmpty(this._prefix) && this._prefix )
{
xGetElementById(this.CSS_HTML_PREFIX+"-prefix").value = this._prefix;
}
else
{
xGetElementById(this.CSS_HTML_PREFIX+"-prefix").value = "";
}
}
}
hotswap.prototype.getPrefix = function()
{
return this._prefix;
}
hotswap.prototype.createGui = function( nDistanceFromTopInPercent )
{
if( xIsEmpty(nDistanceFromTopInPercent) )
{
nDistanceFromTopInPercent = 20;
}
var gui = xGetElementById(this.CSS_HTML_PREFIX + "_wrapper");
if( gui )
{
xRemove(xGetElementById(this.CSS_HTML_PREFIX + "_wrapper"));
}
gui = xCreateElement("div");
xSetAttribute( gui, "id", this.CSS_HTML_PREFIX + "_wrapper" );
var guiHtml = xReplace( this._guiHtml, "PREFIX", this.CSS_HTML_PREFIX );
guiHtml = xReplace( guiHtml, ''20%;/*distance from top*/'', nDistanceFromTopInPercent+''%;/*distance from top*/'' );
gui.innerHTML = guiHtml;
xAppendChild( xGetElementsByTagName("body")[0], gui );
if( !xIsEmpty(this._guiCache) )
{
this._guiCache = {};
}
this._guiCreateFilesList();
if( !xIsEmpty(this._prefix) && this._prefix )
{
xGetElementById(this.CSS_HTML_PREFIX+"-prefix").value = this._prefix;
}
var self = this;
xAddEventListener( xGetElementById(this.CSS_HTML_PREFIX+"-toggle"), "click", function(evt)
{
var gui = xGetElementById(self.CSS_HTML_PREFIX);
if( xHasClass(gui, "mini") )
{
xRemoveClass( gui, "mini" );
}
else
{
xAddClass( gui, "mini" );
}
});
xAddEventListener( xGetElementById(this.CSS_HTML_PREFIX+"-prefix"), "blur", function(evt)
{
self._guiPrefixChanged(evt.target);
});
xAddEventListener( xGetElementById(this.CSS_HTML_PREFIX+"-submit-selected"), "click", function(evt)
{
self._guiRefreshSelected()
});
xAddEventListener( xGetElementById(this.CSS_HTML_PREFIX+"-submit-start"), "click", function(evt)
{
if( xGetAttribute(evt.target, "class") != this.CSS_HTML_PREFIX+"-seconds" )
{
var input, nSeconds = 1;
var children = evt.target.children;
for(var i=0; i<children.length; ++i)
{
if( xGetAttribute(children[i], "class") == this.CSS_HTML_PREFIX+"-seconds" )
{
nSeconds = children[i].value;
}
}
self._guiRefreshSelected();
self._guiRefreshStart( nSeconds );
}
});
xAddEventListener( xGetElementById(this.CSS_HTML_PREFIX+"-submit-stop"), "click", function(evt)
{
self._guiRefreshStop();
});
xAddEventListener( xGetElementById(this.CSS_HTML_PREFIX+"-submit-refresh-list"), "click", xBind(self.guiRefreshFilesList,self) );
}
hotswap.prototype._guiCreateFilesList = function()
{
this._guiCache.files = [];
this._guiCache.activeFiles = {
"css" : [],
"js" : [],
"img" : []
};
var self = this;
var createFilesList = function(list, files)
{
var i, j, r, clone, template, file, fileName, nodesToRemove = [];
for(j=0; j<list.children.length; ++j)
{
if( xHasClass( list.children[j], "template" ) )
{
template = list.children[j];
}
else
{
if( !xHasClass( list.children[j], self.CSS_HTML_PREFIX + "-headline" ) )
{
nodesToRemove.push(list.children[j]);
}
}
}
for(r=0; r<nodesToRemove.length; ++r)
{
xRemove( nodesToRemove[r] );
}
for(i=0; i<files.length; ++i)
{
file = files[i];
clone = xCloneNode( template );
xRemoveClass( clone, "template" );
fileName = self._updatedUrl( xGetAttribute( file.node, file.srcAttributeName ), true );
if( !xContains(self._guiCache.files,fileName) )
{
self._guiCache.files.push(fileName);
clone.innerHTML = fileName;
xAppendChild( list, clone );
xAddEventListener( clone, "click", (function(type, fileName){
return function(evt){
xStopPropagation(evt);
xPreventDefault(evt);
self._guiClickedFile(evt.target, type, fileName);
};
})(file.type, fileName)
);
}
}
}
createFilesList( xGetElementById(this.CSS_HTML_PREFIX+"-css"), this._getFilesByType("css") );
createFilesList( xGetElementById(this.CSS_HTML_PREFIX+"-js"), this._getFilesByType("js", ["hotswap.js"]) );
createFilesList( xGetElementById(this.CSS_HTML_PREFIX+"-img"), this._getFilesByType("img") );
}
hotswap.prototype.deleteGui = function()
{
var gui = xGetElementById(this.CSS_HTML_PREFIX + "_wrapper");
if( gui )
{
xRemove(xGetElementById(this.CSS_HTML_PREFIX + "_wrapper"));
}
}
hotswap.prototype._guiPrefixChanged = function(ele)
{
if( ele )
{
this.setPrefix(ele.value);
}
},
hotswap.prototype._guiClickedFile = function( ele, sType, sFileName )
{
var activeFiles = this._guiCache.activeFiles[sType];
if( xContains( activeFiles, sFileName ) )
{
xRemoveClass(ele, "active");
activeFiles.splice( activeFiles.indexOf(sFileName), 1 )
}
else
{
xAddClass(ele, "active");
activeFiles.push( sFileName );
}
},
hotswap.prototype._guiRefreshSelected = function()
{
var activeFiles = this._guiCache.activeFiles;
if( activeFiles[''css''].length > 0 )
{
this.refreshCss( activeFiles[''css''] );
}
if( activeFiles[''js''].length > 0 )
{
this.refreshJs( activeFiles[''js''] );
}
if( activeFiles[''img''].length > 0 )
{
this.refreshImg( activeFiles[''img''] );
}
},
hotswap.prototype._guiRefreshStart = function( nSeconds )
{
if( this._guiGuiRefreshInterval !== null )
{
this._guiRefreshStop();
}
var self = this;
this._guiGuiRefreshInterval = setInterval( xBind(this._guiRefreshSelected, this), nSeconds * 1000 );
xAddClass( xGetElementById(this.CSS_HTML_PREFIX+"-submit-start"), "inactive" );
xRemoveClass( xGetElementById(this.CSS_HTML_PREFIX+"-submit-stop"), "inactive" );
},
hotswap.prototype._guiRefreshStop = function()
{
if( this._guiGuiRefreshInterval !== null )
{
clearInterval(this._guiGuiRefreshInterval);
}
this._guiGuiRefreshInterval = null;
xRemoveClass( xGetElementById(this.CSS_HTML_PREFIX+"-submit-start"), "inactive" );
xAddClass( xGetElementById(this.CSS_HTML_PREFIX+"-submit-stop"), "inactive" );
}
hotswap.prototype.guiRefreshFilesList = function()
{
this._guiCreateFilesList();
}
}).call(this);
Si desea hacer esto con archivos JavaScript completos , consulte esta pregunta por algo similar que le permita obtener los conceptos básicos de la idea.
Puede cargar en caliente fácilmente el código JavaScript con el sistema de módulos front-end RequireJS, escribí un artículo sobre él recientemente (septiembre de 2015)
https://medium.com/@the1mills/hot-reloading-with-react-requirejs-7b2aa6cb06e1
básicamente borras la caché para un módulo AMD y RequireJS irá a buscar la nueva desde el sistema de archivos.
el truco es usar websockets (socket.io funciona bien) para decirle al navegador que el archivo ha cambiado, y para eliminar el caché y volver a requerir el archivo.
el resto de la información está en el artículo