requireJS and LESS - javascript

I'm using the client-side javascript version of LESS to compile out less code, and would like to continue using this even on the final live site (I know... bad form, but it gives me the ability to allow users to customize a few less variables and have that "theme" their whole app on the fly, seeing as it's a webapp that once loaded never refreshes, I'm thinking the additional second of load time to compile the less is acceptable).
I'm also using requireJS.
The question is:
A) How do I get requireJS to load less code?
B) Does less dispatch any events when compilation is complete? and
C) Is there a way to trigger less to re-compile on command?
Thanks.

I have used the text loader plugin for RequireJS to load the .less file as text, then create a new less.Parser instance to parse the text, then add the style text myself:
(new less.Parser()).parse(lessText, function (err, css) {
if (err) {
if (typeof console !== 'undefined' && console.error) {
console.error(err);
}
} else {
var style = document.createElement('style');
style.type = 'text/css';
style.textContent = css.toCSS();
}
});
You could take a similar approach, but give the style node an ID and remove that ID and then add back another reparsed LESS text when you want on demand.
A caveat: the text plugin can only load text files on demand when the text file is on the same domain as the web page. If you use the RequireJS optimizer, you can inline the text into a built, optimized JS file.

#jrburke: I've put together a quick requirejs plugin based on your code:
define({
version: '0.1',
load: function(name, req, onLoad, config) {
req(['text!' + name, 'base/less'], function(lessText) {
var styleElem;
(new less.Parser()).parse(lessText, function (err, css) {
if (err) {
if (typeof console !== 'undefined' && console.error) {
console.error(err);
}
} else {
styleElem = document.createElement('style');
styleElem.type = 'text/css';
if (styleElem.styleSheet)
styleElem.styleSheet.cssText = css.toCSS();
else
styleElem.appendChild( document.createTextNode( css.toCSS() ) );
document.getElementsByTagName("head")[0].appendChild( styleElem );
}
onLoad(styleElem);
});
});
}
});
"base/less" points to the less source. You could also load this ahead of time, and assume the global less object exists. Ideally, I'd like to pull the less Parser object into this plugin itself so it doesn't create a global at all.
Using this I can do stuff like:
require(['less!global.less'], function(elem) {
});
At which point, global.less has been parsed and added to the page and gives back elem pointing to the style element in case I need to remove or modify it for some reason.
Does anyone have any input or know a better way of doing this?
Cheers

I was having trouble with #nicholas plugin with imports. I fixed it by adding the path of the file to the search path, and set filename for better error messages:
// http://stackoverflow.com/questions/5889901/requirejs-and-less
// enables require(['share/less!whatever.less'], function(elem) {});
define({
version: '0.1',
load: function(name, req, onLoad, config) {
req(['share/text!' + name, 'share/less-1.3.0'], function(lessText) {
var styleElem;
var parser = new(less.Parser)({
filename: name,
paths: [name.split('/').slice(0,-1).join('/') + '/'],
});
parser.parse(lessText, function (err, css) {
if (err) {
if (typeof console !== 'undefined' && console.error) {
console.error(err);
}
} else {
styleElem = document.createElement('style');
styleElem.type = 'text/css';
if (styleElem.styleSheet)
styleElem.styleSheet.cssText = css.toCSS();
else
styleElem.appendChild( document.createTextNode( css.toCSS() ) );
document.getElementsByTagName("head")[0].appendChild( styleElem );
}
onLoad(styleElem);
});
});
}
});

Related

Listen for hot update events on the client side with webpack-dev-derver?

This is a bit of an edge case but it would be helpful to know.
When developing an extension using webpack-dev-server to keep the extension code up to date, it would be useful to listen to "webpackHotUpdate"
Chrome extensions with content scripts often have two sides to the equation:
Background
Injected Content Script
When using webpack-dev-server with HMR the background page stays in sync just fine. However content scripts require a reload of the extension in order to reflect the changes. I can remedy this by listening to the "webpackHotUpdate" event from the hotEmmiter and then requesting a reload. At present I have this working in a terrible and very unreliably hacky way.
var hotEmitter = __webpack_require__(XX)
hotEmitter.on('webpackHotUpdate', function() {
console.log('Reloading Extension')
chrome.runtime.reload()
})
XX simply represents the number that is currently assigned to the emitter. As you can imagine this changed whenever the build changes so it's a very temporary proof of concept sort of thing.
I suppose I could set up my own socket but that seems like overkill, given the events are already being transferred and I simply want to listen.
I am just recently getting more familiar with the webpack ecosystem so any guidance is much appreciated.
Okay!
I worked this out by looking around here:
https://github.com/facebookincubator/create-react-app/blob/master/packages/react-dev-utils/webpackHotDevClient.js
Many thanks to the create-react-app team for their judicious use of comments.
I created a slimmed down version of this specifically for handling the reload condition for extension development.
var SockJS = require('sockjs-client')
var url = require('url')
// Connect to WebpackDevServer via a socket.
var connection = new SockJS(
url.format({
// Default values - Updated to your own
protocol: 'http',
hostname: 'localhost',
port: '3000',
// Hardcoded in WebpackDevServer
pathname: '/sockjs-node',
})
)
var isFirstCompilation = true
var mostRecentCompilationHash = null
connection.onmessage = function(e) {
var message = JSON.parse(e.data)
switch (message.type) {
case 'hash':
handleAvailableHash(message.data)
break
case 'still-ok':
case 'ok':
case 'content-changed':
handleSuccess()
break
default:
// Do nothing.
}
}
// Is there a newer version of this code available?
function isUpdateAvailable() {
/* globals __webpack_hash__ */
// __webpack_hash__ is the hash of the current compilation.
// It's a global variable injected by Webpack.
return mostRecentCompilationHash !== __webpack_hash__
}
function handleAvailableHash(data){
mostRecentCompilationHash = data
}
function handleSuccess() {
var isHotUpdate = !isFirstCompilation
isFirstCompilation = false
if (isHotUpdate) { handleUpdates() }
}
function handleUpdates() {
if (!isUpdateAvailable()) return
console.log('%c Reloading Extension', 'color: #FF00FF')
chrome.runtime.reload()
}
When you are ready to use it (during development only) you can simply add it to your background.js entry point
module.exports = {
entry: {
background: [
path.resolve(__dirname, 'reloader.js'),
path.resolve(__dirname, 'background.js')
]
}
}
For actually hooking into the event emitter as was originally asked you can just require it from webpack/hot/emitter since that file exports an instance of the EventEmitter that's used.
if(module.hot) {
var lastHash
var upToDate = function upToDate() {
return lastHash.indexOf(__webpack_hash__) >= 0
}
var clientEmitter = require('webpack/hot/emitter')
clientEmitter.on('webpackHotUpdate', function(currentHash) {
lastHash = currentHash
if(upToDate()) return
console.log('%c Reloading Extension', 'color: #FF00FF')
chrome.runtime.reload()
})
}
This is just a stripped down version straight from the source:
https://github.com/webpack/webpack/blob/master/hot/dev-server.js
I've fine-tuned the core logic of the crx-hotreload package and come up with a build-tool agnostic solution (meaning it will work with Webpack but also with anything else).
It asks the extension for its directory (via chrome.runtime.getPackageDirectoryEntry) and then watches that directory for file changes. Once a file is added/removed/changed inside that directory, it calls chrome.runtime.reload().
If you'd need to also reload the active tab (when developing a content script), then you should run a tabs.query, get the first (active) tab from the results and call reload on it as well.
The whole logic is ~35 lines of code:
/* global chrome */
const filesInDirectory = dir => new Promise(resolve =>
dir.createReader().readEntries(entries =>
Promise.all(entries.filter(e => e.name[0] !== '.').map(e =>
e.isDirectory
? filesInDirectory(e)
: new Promise(resolve => e.file(resolve))
))
.then(files => [].concat(...files))
.then(resolve)
)
)
const timestampForFilesInDirectory = dir => filesInDirectory(dir)
.then(files => files.map(f => f.name + f.lastModifiedDate).join())
const watchChanges = (dir, lastTimestamp) => {
timestampForFilesInDirectory(dir).then(timestamp => {
if (!lastTimestamp || (lastTimestamp === timestamp)) {
setTimeout(() => watchChanges(dir, timestamp), 1000)
} else {
console.log('%c 🚀 Reloading Extension', 'color: #FF00FF')
chrome.runtime.reload()
}
})
}
// Init if in dev environment
chrome.management.getSelf(self => {
if (self.installType === 'development' &&
'getPackageDirectoryEntry' in chrome.runtime
) {
console.log('%c 📦 Watching for file changes', 'color: #FF00FF')
chrome.runtime.getPackageDirectoryEntry(dir => watchChanges(dir))
}
})
You should add this script to your manifest.json file's background scripts entry:
"background": ["reloader.js", "background.js"]
And a Gist with a light explanation in the Readme: https://gist.github.com/andreasvirkus/c9f91ddb201fc78042bf7d814af47121

ECMA in Datapower

anybody know how to access XML data using xpath expression in ECMA Script(datapower)?
IBM infocenter doesn't have this information on how to access XML data
Please provide if you have any sample script for accessing XML data
Thanks
GatewayScript doesn't support any XML Dom in the ECMA (Node.js) implemented.
I have however used the modules XPATH and DOM with great success.
Download XMLDom (https://github.com/jindw/xmldom) and Xpath (https://github.com/goto100/xpath) Node.js modules and add the following scripts to your DP directory:
dom-parser.js
dom.js
sax.js
xpath.js
To use it in DataPower GWS you first need to get the XML data from INPUT:
// This is where we start, grab the INPUT as a buffer
session.input.readAsBuffers(function(readAsBuffersError, data) {
if (readAsBuffersError) {
console.error('Error on readAsBuffers: ' + readAsBuffersError);
session.reject('Error on readAsBuffers: ' + readAsBuffersError);
} else {
if (data.slice(0,5).toString() === '<?xml') {
console.log('It is XML!');
parseXML(data);
}
} //end read as buffers error
}); //end read as buffer function
function parseXML(xml) {
// Load XML Dom and XPath modules
var select = require('local:///xpath.js');
var dom = require('local:///dom-parser.js');
var doc = new dom.DOMParser().parseFromString(xml.toString(), 'text/xml');
// Get attribute
var nodes = select(doc, "//root/element1/#protocol");
try {
var val = nodes[0].value.toString();
console.log('found xml attribute as ['+val+']');
} catch(e) {
// throw error here
}
// Get an element
nodes = select(doc, "//root/element1/child1");
try {
var val = nodes[0].firstChild.data;
console.log('elemnt found as ['+val+']');
} catch(e) {
//throw error here
}
}
That should be a working sample... You need to change the path for the modules if you move them.
I have a directory in store:/// where I add my GWS modules.
Hope you'll get it to fly!
At least from 7.0.0 firmware version Gatewayscript is able to work with XPATH and DOM easily. Snippet from the DP store:
//reading body from the rule input
session.input.readAsXML(function (error, nodeList) {
if (error) {
//error behaviour
} else {
var domTree;
try {
domTree = XML.parse(nodeList);
} catch (error) {
//error behaviour
}
var transform = require('transform'); //native gatewayscript module
transform.xpath('/someNode/anotherNode/text()', domTree, function(error, result){
if(error){
//error behaviour
}
//some use of result, for example putting it to output
session.output.write(result);
}
});
});

Codemirror- linting - Is there a event to trigger linting explictly?

I am designing an application using CodeMirror that comes with toolbar. Because of performance reason, I am not executing the lint to either through async or asynch mode.
I have provided an icon in the toolbar, on clicking this I am doing the parsing and constructing the errors. But I am stuck with how I can update lint error in editor?
Any pointer would be really helpful.
The lint addon adds an "extension" method to all Editor instances called performLint, combine with editor options of lint: { lintOnChange: false }, and then you can invoke the linter by calling mirror.performLint().
If you define your own lint methods ala
CodeMirror.registerHelper('lint', 'mode', (text) => { /* your cool stuff here */ })
that'll get invoked on performLint().
Have you tried setting the lint value dynamically using the below code?
//enable
editor.setOption("lint",true);
//disable
editor.setOption("lint",false);
You can see a demo here JSFiddle link
To trigger lint in one line:
// trigger registered lint handler
editor.setOption("lint", {});
// trigger custom lint handler defined on the fly
editor.setOption("lint", {
getAnnotations: function() { /* some smart code */ return found; }
});
If you wonder why it should ever work, have look into addon/lint/lint.js, specially the following lines:
CodeMirror.defineOption("lint", false, function(cm, val, old) {
...
if (val) {
...
startLinting(cm);
}
});
It sounds like a good feature that doesn't appear to exist.
It is straighforward to create a button to launch a validation from an external button.
For example [no credit] https://jsfiddle.net/q43drhyk/
function checkFormat(editor) {
var success = JSHINT(editor.getValue());
var output = '';
if (!success) {
output = "Check format error:\n\n";
for (var i in JSHINT.errors) {
var err = JSHINT.errors[i];
if (null != err) {
output += err.line + '[' + err.character + ']: ' + err.reason + '\n';
} else {
output += "Check format unknown error:\n";
}
}
alert(output);
}
return success;
}
However this does not render the validation messages in the CodeMirror editor to display in the linenumber gutter, which is what the OP was looking for.
To do that, you could customise the lint add-on code.
For example from the json-lint provided in the standard addons
[https://github.com/codemirror/CodeMirror/blob/master/addon/lint/json-lint.js]
1.Extract the body of registerHelper() into a new function that reproduces the existing function:
CodeMirror.registerHelper("lint", "json", function(text) {
return mylinthandler(text)
}
mylinthandler(text) {
var found = [];
jsonlint.parseError = function(str, hash) {
var loc = hash.loc;
found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
message: str});
};
try { jsonlint.parse(text); }
catch(e) {}
return found;
}
Turn off the auto-lint CodeMirror options with.
lintOnChange: false
Then when you wish to lint, call
mylinthandler(editor.getValue())

How to select the first element of an object in javascript on IE8 without using []?

I'm fighting with IE8 and the flashcanvas.js plugin trying to capture a signature.
The demo works fine on IE8, but since I'm using a bunch of other plugins including requireJS, I'm running into some kind of problem on IE8.
I have nailed it down to this:
function onReadyStateChange() {
if (document.readyState === "complete") {
document.detachEvent(ON_READY_STATE_CHANGE, onReadyStateChange);
var canvases = document.getElementsByTagName(CANVAS);
// => returns 1 in IE8 debugger
console.log( canvases.length )
// => returns objectHTMLCollection
console.log( canvases )
for (var i = 0, n = canvases.length; i < n; ++i) {
console.log(" run "+i)
// => this produces an error...
console.log( canvases[i])
// trigger
FlashCanvas.initElement(canvases[i]);
}
}
}
I don't understand why it's not working, but canvases[i] throws an [object HTMLUnknownElement] error.
Question:
Any idea what might be the cause? As a workaroud, how could I select the first element of my objectHTMLCollection without using [] and staying in Javascript!
I have also tried adding an id to the canvas element or select it by class (using Jquery). Same result, length=1, select=forget it.
Thanks for help!
EDIT:
I'm requesting plugins like this from my main app controller:
$(document).on('pagebeforeshow', '#basket', function(e, data){
// signaturePad
var signIt = $('.signatureWrapper');
if ( signIt.length > 0 && signIt.jqmData('bound') != true ) {
signIt.jqmData('bound', true);
require(['services/signature/app'], function (App) {
App.render({target: signIt, drawOnly: true });
});
};
This calls an app.js file, which defines all dependencies required and once everything has loaded, fire the plugin:
define([ 'services/signature/app'
, 'services/signature/jquery.signaturepad.min'
, 'services/signature/json2.min'
, 'services/signature/flashcanvas'
], function( app, signature, json2, flashcanvas ) {
function render(parameters) {
parameters.target.signaturePad({ drawOnly:parameters.drawOnly });
};
return {
render:render
};
});
So, I'm wasting an http-request for flashcanvas.js when I don't really need it. But all files are loaded allright, I believe...
How are you adding the canvas element to the DOM? Since IE8 doesn't support the canvas element, you need to create it dynamically and append it to the DOM, as shown in the FlashCanvas docs
var canvas = document.createElement("canvas");
document.getElementById("target").appendChild(canvas);
if (typeof FlashCanvas != "undefined") {
FlashCanvas.initElement(canvas);
}
Array document.getElementsByName(String balise)
http://www.toutjavascript.com/reference/reference.php?iref=156
You wrote :
var canvases = document.getElementsByTagName(CANVAS);
Are you sure CANVAS is a String ? Maybe you want to write
var canvases = document.getElementsByTagName("CANVAS");
Try to convert the collection to an array:
var arr = Array.prototype.slice.call( canvases );

Partial update while supporting seo

Using NodeJs I'm trying to do something quite similar to Meteor: I want to send only the parts of a page that actually changed. My dilemma is that I know how to create such a framework to respond to link clicks and send partial updates but such a framework won't cater to direct browsing to a page other than the index (which is what is required for search engines and people without javascript to use your site).
I can also figure out how to make a framework to support entire page reloads, handlebars and a simple node server instance would take care of that. Hoeever, I can't figure out how to create a way that would allow me to write one method to tell the framework the partial updates for a page and let the framework figure out what else needs to be loaded.
A way I can think of would be to create the index page every time (for entire page loads) and apply partial updates to that but that can quickly become expensive if a subpage differs a lot from a very crowded index.
An example method would look something like this:
function images(id) {
if (id == null) {
// load all images from database
template.images = db.result();
template.content = template.loadblock('gallery');
}
else {
// retrieve single image
template.content = template.loadblock('single_image');
template.image = db.result();
}
}
On a partisl updste calling this method for domain.com/images would work just fine because it's clear what had changed.
For an entire page load this function would miss things like a header, footer ,navigation, etc.
In an answer I would look for an example where this has been done or some tips that Can point me in the right direction. I'm sorry for any typoes I wrote this post on an ipad. If you have any questions about my question just ask and I'll update as needed.
Update:
A possible example of a solution might be the following code. It's to give an idea, it probably won't actually run
// As a convention, don't pass around raw values if they aren't static but pass around functions such as
data.images = function () {
// run db query
// return an object with the images
}
// This constraint might be limited to the index() method
var routes = {
// This now allows us to do things like this:
index: function() {
var data;
// Initialise everything needed for the index
data.title = 'Index';
data.nav = { Index: '/', Images: '/images' };
data.content = 'Hello World';
},
categories: function() {
var data;
data.content = render('gallery', function () { /* load and return images as object */ }); // Not sure about this dynamic subtemplating but oh well
}
// This now allows us to do the following:
function request(page, type) {
if (type == 'update') {
if (routes[page] != undefined && typeof routes[page] == 'function') {
respond(routes[page]());
}
}
else {
if (routes[page] != undefined && typeof routes[page] == 'function') {
var data = mergeArrays(routes['index'](), routes[page]());
// index.html which is just a Handlebars template
respond(data);
}
}
}
Here is a pattern I often use (in Express apps):
function respond(req, res, name, resource) {
if(req.accepts('json')) {
// Send JSON to clients who want it
res.send(resource);
} else {
// Render with layout only for non-XHR requests
resource.layout = !req.xhr;
res.render('resources/' + name, resource);
}
}
Example usage:
app.get('/images', function(req, res, next) {
getImages(function(err, images) {
if(err) return next(err);
respond(req, res, 'images', images);
});
});
app.get('/images/:id', function(req, res, next) {
getImage(req.params.id, function(err, image) {
if(err) return next(err);
respond(req, res, 'image', image);
});
});
image.jade:
img(src=uri, alt=title)
images.jade:
#gallery
for image in images
include image
Clients who ask for JSON get that, otherwise they get the full page only if it's a non-XHR request. XHR requests get just the HTML snippet for the requested resource. This works well for quite simple apps, where resources mostly correspond to pages.

Categories

Resources