This year I want the Wikimedia technical community to have a strong presence at Open Source Bridge http://opensourcebridge.org/ in Portland, Oregon, USA, June 26-29. OSB is tech talks & hack sessions with hands-on technologists we want, for Foundation staff recruiting (the Portland tech scene has good people looking for jobs) and for volunteer recruiting and collaboration (tons of Mozilla people went there last year). Good talks, clueful people, great food. :-)
If you submit a talk and it gets accepted, tell me, and Wikimedia Foundation will partially subsidize or fully pay for your flight and hotel. If you submit a talk and it doesn't get accepted but you still want to go, talk with me and I'll see what I can do.
Call for talks: http://opensourcebridge.org/blog/2012/01/announcing-the-2012-call-for-proposals/ Ideas: the parser rewrite, Wikimedia Labs, how we scale and optimize performance on a shoestring budget, our git/gerrit migration, securing PHP-based webapps, various approaches to making our data more structured/semantic, collaborative design, lessons from our communities, JS hacks, hetdeploy, offline/mobile, geodata...
Please forward.
On 03/10/2012 08:25 AM, Sumana Harihareswara wrote:
This year I want the Wikimedia technical community to have a strong presence at Open Source Bridge http://opensourcebridge.org/ in Portland, Oregon, USA, June 26-29. OSB is tech talks & hack sessions with hands-on technologists we want, for Foundation staff recruiting (the Portland tech scene has good people looking for jobs) and for volunteer recruiting and collaboration (tons of Mozilla people went there last year). Good talks, clueful people, great food. :-)
If you submit a talk and it gets accepted, tell me, and Wikimedia Foundation will partially subsidize or fully pay for your flight and hotel. If you submit a talk and it doesn't get accepted but you still want to go, talk with me and I'll see what I can do.
Call for talks: http://opensourcebridge.org/blog/2012/01/announcing-the-2012-call-for-proposals/ Ideas: the parser rewrite, Wikimedia Labs, how we scale and optimize performance on a shoestring budget, our git/gerrit migration, securing PHP-based webapps, various approaches to making our data more structured/semantic, collaborative design, lessons from our communities, JS hacks, hetdeploy, offline/mobile, geodata...
Please forward.
(I should add -- also there would be complications if somehow lots of Wikimedia people get their talks accepted and I can't budget to subsidize all of them, or you can't get a visa to the US in time, or some unforeseen thing comes up like that. But I don't predict those problems happening.)
On 03/10/2012 11:50 AM, Sumana Harihareswara wrote:
On 03/10/2012 08:25 AM, Sumana Harihareswara wrote:
This year I want the Wikimedia technical community to have a strong presence at Open Source Bridge http://opensourcebridge.org/ in Portland, Oregon, USA, June 26-29. OSB is tech talks & hack sessions with hands-on technologists we want, for Foundation staff recruiting (the Portland tech scene has good people looking for jobs) and for volunteer recruiting and collaboration (tons of Mozilla people went there last year). Good talks, clueful people, great food. :-)
If you submit a talk and it gets accepted, tell me, and Wikimedia Foundation will partially subsidize or fully pay for your flight and hotel. If you submit a talk and it doesn't get accepted but you still want to go, talk with me and I'll see what I can do.
Call for talks: http://opensourcebridge.org/blog/2012/01/announcing-the-2012-call-for-proposals/ Ideas: the parser rewrite, Wikimedia Labs, how we scale and optimize performance on a shoestring budget, our git/gerrit migration, securing PHP-based webapps, various approaches to making our data more structured/semantic, collaborative design, lessons from our communities, JS hacks, hetdeploy, offline/mobile, geodata...
Please forward.
(I should add -- also there would be complications if somehow lots of Wikimedia people get their talks accepted and I can't budget to subsidize all of them, or you can't get a visa to the US in time, or some unforeseen thing comes up like that. But I don't predict those problems happening.)
Quick reminder: today's the deadline. Ward Cunningham, Rob Lanphier, and I have proposed talks.
On 03/16/2012 11:36 AM, Sumana Harihareswara wrote:
On 03/10/2012 11:50 AM, Sumana Harihareswara wrote:
On 03/10/2012 08:25 AM, Sumana Harihareswara wrote:
This year I want the Wikimedia technical community to have a strong presence at Open Source Bridge http://opensourcebridge.org/ in Portland, Oregon, USA, June 26-29. OSB is tech talks & hack sessions with hands-on technologists we want, for Foundation staff recruiting (the Portland tech scene has good people looking for jobs) and for volunteer recruiting and collaboration (tons of Mozilla people went there last year). Good talks, clueful people, great food. :-)
If you submit a talk and it gets accepted, tell me, and Wikimedia Foundation will partially subsidize or fully pay for your flight and hotel. If you submit a talk and it doesn't get accepted but you still want to go, talk with me and I'll see what I can do.
Call for talks: http://opensourcebridge.org/blog/2012/01/announcing-the-2012-call-for-proposals/ Ideas: the parser rewrite, Wikimedia Labs, how we scale and optimize performance on a shoestring budget, our git/gerrit migration, securing PHP-based webapps, various approaches to making our data more structured/semantic, collaborative design, lessons from our communities, JS hacks, hetdeploy, offline/mobile, geodata...
Please forward.
(I should add -- also there would be complications if somehow lots of Wikimedia people get their talks accepted and I can't budget to subsidize all of them, or you can't get a visa to the US in time, or some unforeseen thing comes up like that. But I don't predict those problems happening.)
Quick reminder: today's the deadline. Ward Cunningham, Rob Lanphier, and I have proposed talks.
Sorry for the spam, but they've extended the deadline to the 30th: http://opensourcebridge.org/blog/2012/03/cfp-deadline-extended-until-march-3...
Hi! Can a JavaScript / ResourceLoader guru explain what's wrong with my code?
Module definitions at server side: http://pastebin.com/8cmRbNxe
modules are loaded correctly by the following code: http://pastebin.com/MFWk6znv It is checked many times during extension development, 'localBasePath' and 'remoteExtPath' is set up correctly.
* By default, the module with the following code is loaded: http://pastebin.com/0u96aMep
Then it loads either 'ext.jqgmap.view' module or 'ext.jqgmap.edit' module, depending on data provided in generated html page.
This is made for two reasons: 1. Editor code is larger and when the page does not need the editor, it loads only viewer code, which is faster to load. 2. Editor modifies the mw.jqgmap.MapController.prototype.createEditor and in the future may also introduce or modify more prototypes. This makes code more modular.
However, I have very big problem loading 'ext.jqgmap.view' from 'ext.jqgmap.edit':
1. mw.loader.using( 'ext.jqgmap.view', function() { 2. console.log(mw.jqgmap);
When callback function is executed, Chrome console does not show mw.jqgmap as being defined. So console.log(mw.jqgmap) returns 'undefined' and only when that happens, actual execution of 'ext.jqgmap.view' begins. It seems that ResourceLoader loads the code wrapped into exception handler which tries to detect when the code has to be loaded. That would be ok, however, when I execute 'ext.jqgmap.view' code step by step:
1. if ( mw.jqgmap ) { 2. return; 3. } 4. 5. mw.jqgmap = (function () {
First line if ( mw.jqgmap ) { }
is executed second time, when mw.jqgmap value is set with closure result. Then the further execution of code just stops. No further errors in Chrome console. And, even mw.jqgmap is correctly defined!
There used to be no if (mw.jqgmap) ( return; ) check originally, then another code (Google maps) complained about being included twice, which may cause "unpredictable results".
How can I fix the chain of loading so it will correctly setup mw.jqgmap.MapController and mw.jqgmap.MarkerController so they will be different in edit and view mode and available to use in 'ext,jqgmap' code?
I also have the whole extension under development however I haven't comitted it into svn yet - I don't know whether yet another map extension is desirable there. Although there was no rule against duplicate functionality in extensions?
ResourceLoader is a bit hard to me.
Dmitriy
On 18.03.2012 18:26, Dmitriy Sintsov wrote:
replaced 'new MarkerController(...)' calls to 'new mw.jqgmap.MarkerController(...)' calls left from incomplete refactoring of early working (non-broken) revision, however refactored separate view / edit modules code still does not work, with the same js code being stopped without additional Chrome errors - so it hard to guess the reason what's broken in my js code.
Dmitriy
Hi! I've tweaked my code few times, trying to make it simpler: got rid of some closure calls and left only one dynamic module load at client-side.
http://pastebin.com/UxyifLmx http://pastebin.com/q3Tm6Ajd http://pastebin.com/4emMDBS6
Still, it gives me headaches, because mw.loader.using( 'ext.jqgmap.edit' ,function(...)) does not actually execute the loaded module's code even when the callback function is "fired". This means that additional mw.jqgmap prototypes defined in 'ext.jqgmap.edit' are not available when actual object instances are created in function createMapControllers(), thus throws an error. When I add explicit call to dummy function loadEditPrototypes() defined in 'ext.jqgmap.edit', the module is executed, but the further scipts execution abruptly ends without any error in Chrome console.
To me it seems that ResourceLoader tries to be too smart not actually executing the code just before .using() callback. How does it figure out that I defined function loadEditPrototypes() in 'ext.jqgmap.edit' module? That kind of 'automagic' is tricky. Why not to have something like $wgAutoloadClasses but for ResourceLoader? Or, even to be able to really execute module on load. And why does it stop execution path is a puzzle to me..
Dmitriy
On Mon, 19 Mar 2012 00:40:54 -0700, Dmitriy Sintsov questpc@rambler.ru wrote:
Hi! I've tweaked my code few times, trying to make it simpler: got rid of some closure calls and left only one dynamic module load at client-side.
http://pastebin.com/UxyifLmx http://pastebin.com/q3Tm6Ajd http://pastebin.com/4emMDBS6
Still, it gives me headaches, because mw.loader.using( 'ext.jqgmap.edit' ,function(...)) does not actually execute the loaded module's code even when the callback function is "fired". This means that additional mw.jqgmap prototypes defined in 'ext.jqgmap.edit' are not available when actual object instances are created in function createMapControllers(), thus throws an error. When I add explicit call to dummy function loadEditPrototypes() defined in 'ext.jqgmap.edit', the module is executed, but the further scipts execution abruptly ends without any error in Chrome console.
To me it seems that ResourceLoader tries to be too smart not actually executing the code just before .using() callback. How does it figure out that I defined function loadEditPrototypes() in 'ext.jqgmap.edit' module? That kind of 'automagic' is tricky. Why not to have something like $wgAutoloadClasses but for ResourceLoader? Or, even to be able to really execute module on load. And why does it stop execution path is a puzzle to me..
Autoloading classes is not possible. Even if every browser supported getters and we could use them to dynamically load classes, this would require synchronous http calls. Which are absolutely HORRIBLE because they block the entire JS thread and in addition typically even block the browser's UI.
Dmitriy
You shouldn't be dropping the closure, you don't want local things in the global scope. You also shouldn't be using the global $ and mw directly. Everything should be using a pattern like: ( function( $, mw ) {
} )( jQuery, mediaWiki );
var jqgmap = []; for ( var mapIndex in jqgmap ) {
This is VERY bad JavaScript coding practice. Please use $.each().
$('<div class="jqgmap_code" id="jqgmap_code' + this.Idx + '">'+ '<input type="checkbox"></input>' + mw.msg( 'jqgmap-show-code' )
'<input type="checkbox"></input>' + mw.msg( 'jqgmap-show-code' )
'<div class="jqgmap_preview"></div>' + '<div class="jqgmap_preview"></div>' + '</div>')
I should really start poking people about this one. `"<div>" . wfMsg( 'foo' ) . "</div>"` is bad in PHP code, and it's just as bad inside JS. You should be creating your html with NO + concatenation, and then using a .find() and .text() to insert things where they belong. That, or use multiple $() calls with .append() to create the full structure. Likewise you shouldn;t be inserting this.Idx that way into the attribute. It should be added with something like .attr( "jqgmap_code" + this.Idx );
* Daniel Friesen lists@nadir-seen-fire.com [Mon, 19 Mar 2012 01:35:21 -0700]:
Autoloading classes is not possible. Even if every browser supported getters and we could use them to dynamically load classes, this would require synchronous http calls. Which are absolutely HORRIBLE because they block the entire JS thread and in addition typically even block the browser's UI.
How does ResourceLoader figures that function loadEditPrototypes() was defined in 'ext.jqgmap.edit' when I call this function from 'ext.jqgmap'? And if there was no extra http call, then why the list of scripts in Chrome debugger does not include 'JQGmap.edit.js' among the scripts before mw.loader.using() and loadEditPrototypes() call was performed?
You shouldn't be dropping the closure, you don't want local things in the global scope. You also shouldn't be using the global $ and mw
directly. Prototypes are not local things. They are carried out with function they belong to. I define few prototypes in 'ext.jqgmap.view' then I want to add more prototypes in 'ext.jqgmap.edit'. But not always, only when the edit mode is active. However you are right about minor local variables.
Everything should be using a pattern like: ( function( $, mw ) {
} )( jQuery, mediaWiki );
Ok, I'll try the closure call with two arguments in every module, didn't think about that. I'll report if that will fix the abrupt termination of module "chain" execution.
var jqgmap = []; for ( var mapIndex in jqgmap ) {
This is VERY bad JavaScript coding practice. Please use $.each().
I know that I can use $.each() here. I don't do that for few reasons: for..in is easier to debug (step by step key) and also because for..in was problematic only for objects {}, not for arrays [] AFAIK. However I might be wrong. Maybe I'll switch to $.each() however the code was working with for..in perfectly well before I refactored it into multiple modules. There was only one module. However it was growing so I had to split it into view and edit submodules.
$('<div class="jqgmap_code" id="jqgmap_code' + this.Idx + '">'+ '<input type="checkbox"></input>' + mw.msg(
'jqgmap-show-code'
)
'<input type="checkbox"></input>' + mw.msg(
'jqgmap-show-code'
)
'<div class="jqgmap_preview"></div>' + '<div class="jqgmap_preview"></div>' + '</div>')
I should really start poking people about this one. `"<div>" . wfMsg( 'foo' ) . "</div>"` is bad in PHP code, and it's just as bad inside
JS.
You should be creating your html with NO + concatenation, and then
using
a .find() and .text() to insert things where they belong. That, or use multiple $() calls with .append() to create the full structure. Likewise you shouldn;t be inserting this.Idx that way into the attribute. It should be added with something like .attr( "jqgmap_code" + this.Idx );
I might consider refactoring of jQuery DOM nodes creation, however the loading of 'ext.jqgmap.edit' module does not work now so I cannot continue until I'll figure out why. This code is not ideal however it's not the reason of the fault. This code was working perfectly well before splitting into multiple modules and introducing mw.loader.using('ext.jqgmap.edit',function(){...}); Dmitriy
On Mon, 19 Mar 2012 02:07:54 -0700, Dmitriy Sintsov questpc@rambler.ru wrote:
- Daniel Friesen lists@nadir-seen-fire.com [Mon, 19 Mar 2012 01:35:21
-0700]:
Autoloading classes is not possible. Even if every browser supported getters and we could use them to dynamically load classes, this would require synchronous http calls. Which are absolutely HORRIBLE because they block the entire JS thread and in addition typically even block the browser's UI.
How does ResourceLoader figures that function loadEditPrototypes() was defined in 'ext.jqgmap.edit' when I call this function from 'ext.jqgmap'? And if there was no extra http call, then why the list of scripts in Chrome debugger does not include 'JQGmap.edit.js' among the scripts before mw.loader.using() and loadEditPrototypes() call was performed?
RL doesn't know anything about loadEditPrototypes, I don't know what would make you think that it does.
I can't really debug and say what's wrong without your code actually running somewhere.
You shouldn't be dropping the closure, you don't want local things in the global scope. You also shouldn't be using the global $ and mw
directly. Prototypes are not local things. They are carried out with function they belong to. I define few prototypes in 'ext.jqgmap.view' then I want to add more prototypes in 'ext.jqgmap.edit'. But not always, only when the edit mode is active. However you are right about minor local variables.
Everything should be using a pattern like: ( function( $, mw ) {
} )( jQuery, mediaWiki );
Ok, I'll try the closure call with two arguments in every module, didn't think about that. I'll report if that will fix the abrupt termination of module "chain" execution.
var jqgmap = []; for ( var mapIndex in jqgmap ) {
This is VERY bad JavaScript coding practice. Please use $.each().
I know that I can use $.each() here. I don't do that for few reasons: for..in is easier to debug (step by step key) and also because for..in was problematic only for objects {}, not for arrays [] AFAIK. However I might be wrong. Maybe I'll switch to $.each() however the code was working with for..in perfectly well before I refactored it into multiple modules. There was only one module. However it was growing so I had to split it into view and edit submodules.
Completely wrong. for..in is object iteration. It is not to be used for array iteration. When you use for..in to iterate over an array you're actually iterating over object keys. Unset things may be left out, if there are any properties set you'll iterate over those when you shouldn't, if someone sets something on Array.prototype. (eg: Someone implements .forEach in an older browser) you'll iterate over that as well, and it's perfectly valid for a browser to iterate over the keys in an order that's different from the actual order of items in the array.
$('<div class="jqgmap_code" id="jqgmap_code' + this.Idx + '">'+ '<input type="checkbox"></input>' + mw.msg(
'jqgmap-show-code'
)
'<input type="checkbox"></input>' + mw.msg(
'jqgmap-show-code'
)
'<div class="jqgmap_preview"></div>' + '<div class="jqgmap_preview"></div>' + '</div>')
I should really start poking people about this one. `"<div>" . wfMsg( 'foo' ) . "</div>"` is bad in PHP code, and it's just as bad inside
JS.
You should be creating your html with NO + concatenation, and then
using
a .find() and .text() to insert things where they belong. That, or use multiple $() calls with .append() to create the full structure. Likewise you shouldn;t be inserting this.Idx that way into the attribute. It should be added with something like .attr( "jqgmap_code" + this.Idx );
I might consider refactoring of jQuery DOM nodes creation, however the loading of 'ext.jqgmap.edit' module does not work now so I cannot continue until I'll figure out why. This code is not ideal however it's not the reason of the fault. This code was working perfectly well before splitting into multiple modules and introducing mw.loader.using('ext.jqgmap.edit',function(){...}); Dmitriy
* Daniel Friesen lists@nadir-seen-fire.com [Mon, 19 Mar 2012 01:35:21 -0700]:
( function( $, mw ) {
} )( jQuery, mediaWiki );
I modified all of three modules: main, view and edit module to the recommended pattern. http://pastebin.com/1kS6EyUu http://pastebin.com/WQzBTw6W http://pastebin.com/UqpTAvZ8
However, the execution still stops after the following call: mw.jqgmap.loadEdit(); performed in main module. When I comment out that call, execution of further scripts does not terminate: however extra prototypes defined in 'ext.jqgmap.edit' become unavailable; thus MapController and MarkerController instances do not work correctly in edit mode.
Still cannot dynamically load 'ext.jqgmap.edit' successfully..
Maybe I'll temporary re-unite the modules into monolithic one, otherwise I risk to ruin the timeline of project. Too bad I wasn't able to defeat the trouble. Dmitriy
<offtopic>
On Mon, Mar 19, 2012 at 9:35 AM, Daniel Friesen lists@nadir-seen-fire.comwrote:
On Mon, 19 Mar 2012 00:40:54 -0700, Dmitriy Sintsov questpc@rambler.ru wrote: var jqgmap = [];
for ( var mapIndex in jqgmap ) {
This is VERY bad JavaScript coding practice. Please use $.each().
This is rather exaggerated. Even more when looking at that suggestion.
Arrays should, indeed, not be enumerated with a for-in loop. Arrays in JS can only contain numeral indices, so they should simply be iterated with a simple for-loop like this `for (i = 0; i < myArr.length; i += 1) { .. }` (maybe cache length for slight performance gain by reducing property lookups).
Using $.each has overhead (+1+n function invocations). When given an array it will do a simple for-loop with a counter. It has overhead of 1+n additional function invocations and context creations. In most cases there is no use for it. There is one case where it is handy and that's when you specifically need a local context for the loop (being careful not to create later-called functions inside the loop, risking all variables being in the post-loop state). If you don't need a local scope for your loop, then using $.each (or the native [].forEach in later browsers) is pointless as it only has overhead of additional function invocations and lowering the position in the scope chain.
When iterating over objects, however, (not arrays) then $.each is no better than a for-in loop because (contrary to what some people think) it is not a shortcut for for-in + if-hasOwn wrapper. When an object is passed, it literally just does a plain for-in loop invoking the callback with the value. jQuery does not support environments where someone extends the native Object.prototype because it is considered harmful (an therefore MediaWiki inherently does not support that either), so a plain for-in loop over an object (excluding array objects) is perfectly fine according to our conventions.
See also http://stackoverflow.com/a/1198447/319266
.... but so much for the good (and bad, evil) parts of javascript :D
-- Krinkle
<offtopic>
On Mon, Mar 19, 2012 at 2:23 PM, Krinkle krinklemail@gmail.com wrote:
On Mon, Mar 19, 2012 at 9:35 AM, Daniel Friesen <lists@nadir-seen-fire.com
wrote:
On Mon, 19 Mar 2012 00:40:54 -0700, Dmitriy Sintsov questpc@rambler.ru wrote: var jqgmap = [];
for ( var mapIndex in jqgmap ) {
This is VERY bad JavaScript coding practice. Please use $.each().
This is rather exaggerated. Even more when looking at that suggestion.
Arrays should, indeed, not be enumerated with a for-in loop. Arrays in JS can only contain numeral indices, so they should simply be iterated with a
s/can/should only contain numeral indices. Arrays as just objects so they can indeed contain anything, and inherit functions. Also note that a for-in loop on arrays will return the keys as strings, not numbers: `var a = ['foo', 'bar']; for (var b in a) {}; console.log(typeof b, b /* "string", "1" */);`
-- Krinkle
* Krinkle krinklemail@gmail.com [Mon, 19 Mar 2012 14:32:51 +0100]: Converted all of for..in into $.each(). The most funny thing is that $.each() did not work correctly with sparse arrays [], walking with "undefs" between real elements. While for..in used to work fine (FF,Chrome,IE8,IE9). So I had to convert sparse arrays into sparse objects {}, just as you recommended. This way $.each() works, however it also creates new context (different "this") so I've had to use var myself = this more often than it used to be.
The most pailful part was to pass control to the main module after edit module was abruptly terminated (without any warnings in Chrome console). I managed to call the main module method directly from edit module. Not a very nice thing, but at least it works. Who would know that such potentially easy thing as refactoring one js class into two ResourceLoader modules can be that much painful. I used to dynamically load jQuery for custom MediaWiki 1.15 installation via LABjs and don't remember such problems back then.
Dmitriy
On Mar 20, 2012, at 11:15 AM, Dmitriy Sintsov wrote:
- Krinkle krinklemail@gmail.com [Mon, 19 Mar 2012 14:32:51 +0100]:
Converted all of for..in into $.each(). The most funny thing is that $.each() did not work correctly with sparse arrays [], walking with "undefs" between real elements. While for..in used to work fine (FF,Chrome,IE8,IE9). So I had to convert sparse arrays into sparse objects {}, just as you recommended. This way $.each() works, however it also creates new context (different "this") so I've had to use var myself = this more often than it used to be.
Few points: * The easiest way to understand it is to consider "sparse arrays" to not exist in javascript. It doesn't throw an exception if you try it, but it's not supposed to be posisble, so don't. * Sparse objects don't exist in any language afaik. What you created is just a regular plain object. Although you may assign values to a numerical key, it really is a string, so be careful that you don't assume they are a number: <code> var a = {}; a[4] = 'foo'; $.each(function (key, val) { // typeof key === "string", key === "4" for ( var key in a ) {}; // // typeof key === "string", key === "4" </code> * As I said before I think there is no point in using $.each() in this case. It is nice if you need a context but as you already found out, more often than not it's actually a burden to have to deal with it if you just want a loop inside an existing context. I would've kept the for-in loop you originally had which was just fine, only change the array into an object.
The most pailful part was to pass control to the main module after edit module was abruptly terminated (without any warnings in Chrome console). I managed to call the main module method directly from edit module. Not a very nice thing, but at least it works. Who would know that such potentially easy thing as refactoring one js class into two ResourceLoader modules can be that much painful. I used to dynamically load jQuery for custom MediaWiki 1.15 installation via LABjs and don't remember such problems back then.
I'd have to look further into the actual code to know what you mean here, but there isn't really such thing as "refactorign a JS class into a ResourceLoader module". For one, javascript doesn't have classes, but that's just word choise. Secondly, ResourceLoader "just" loads javascript. You don't have to refactor anything. There are a few things that it enforces (such as execution of the javascript file in a local context), but other than that you don't have to change anything "for ResourceLoader".
There are a lot of conventions and best practices often associated with "porting to ResourceLoader" but that's more along the lines of "now that we're working on this, lets get rid of crappy code and make it nice", not anything ResourceLoader actually requires.
I'm curious what kind of problems you are referring to here? The kind of problems you didn't have when you loaded jQuery in MediaWiki 1.15
-- Krinkle
* Krinkle krinklemail@gmail.com [Wed, 21 Mar 2012 18:51:45 +0100]:
Few points:
- The easiest way to understand it is to consider "sparse arrays" to
not
exist in javascript. It doesn't throw an exception if you try it, but it's not supposed
to
be posisble, so don't.
In Chrome, executing the following code: var a = []; a[0] = 'a'; a[2] = 'c'; worked, however debugger inspector shows 'undefined' elements between real elements of array. And the length is counted for 0..last element, including the undefs. However, for..in worked for such arrays fine, but may be inefficient - I do not think it has hashes or linked lists to skip the undefs, it probably just skips them one by one. That worked in IE8/IE9/FF/Chrome so I must not be first coder who used that.
I switches to {} and $.each() two days ago, so that's already the past.
- Sparse objects don't exist in any language afaik. What you created
is
just a regular plain object.
I was mentioning JS objects with numeric keys - I didn't know they are silently converted to numbers. Now I removed parseInt() from my code.
- As I said before I think there is no point in using $.each() in this case. It is nice if you need a context but as you already found out,
more often than not it's actually a burden to have to deal with it if you just want a loop inside an existing context. I would've kept the for-in loop you originally had which was just fine, only change the array into an object.
Maybe I'll switch to for ...in back then however I scared by "basic" prototypes so maybe I'll check .hasOwnProperty() then, too. Still haven't decided that.
I'd have to look further into the actual code to know what you mean here, but there isn't really such thing as "refactorign a JS class into a ResourceLoader module". For one, javascript doesn't have classes, but that's just word choise. Secondly, ResourceLoader "just" loads
Actually, creating JS function and assigning the prototypes to it and then instantiating it via new operator and then adding properties to it's context, including some from it's own prototypes works like classes. Prototypes are really similar to virtual methods tables available in another OO languages, but another languages manipulate them indirectly, in higher and better level than JS. Frankly, I am not much a fan of JS, however I have no another widespread choice for client-programming web.
You don't have to refactor anything. There are a few things that it enforces (such as execution of the javascript file in a local
context),
but other than that you don't have to change anything "for
ResourceLoader".
There are a lot of conventions and best practices often associated
with
"porting to ResourceLoader" but that's more along the lines of "now
that
we're working on this, lets get rid of crappy code and make it nice", not anything ResourceLoader actually requires.
I'm curious what kind of problems you are referring to here? The kind
of
problems you didn't have when you loaded jQuery in MediaWiki 1.15
The problems are simple - when I load another module with mw.loader.using, then call it's module from mw.loader.using callback, there is no return to original module. So I am enforced to build inflexible chain of calling main module directly from loaded module main context. I got used to it, although that was a little disappointment.
Now I have another pain: mw.msg() caches messages quite aggressively, even though I put the following lines in LocalSettings.php: $wgResourceLoaderDebug = true; $wgEnableParserCache = false; $wgCachePages = false;
I got the same old version of message, even after multiple Ctrl+R in Chrome, action=purge and even httpd restats. How can I make mw.msg() being updated every time I change i18n file of my extension? Maybe the evil provider caches some requests though :-( I probably should check with Wireshark. It's tiresome that fighting with environment takes too much of the time. Dmitriy
* Dmitriy Sintsov questpc@rambler.ru [Thu, 22 Mar 2012 11:15:40 +0400]:
In Chrome, executing the following code: var a = []; a[0] = 'a'; a[2] = 'c'; worked, however debugger inspector shows 'undefined' elements between real elements of array. And the length is counted for 0..last element, including the undefs. However, for..in worked for such arrays fine,
but
may be inefficient - I do not think it has hashes or linked lists to skip the undefs, it probably just skips them one by one. That worked
in
IE8/IE9/FF/Chrome so I must not be first coder who used that.
I switches to {} and $.each() two days ago, so that's already the
past.
Maybe the efficient and simple way for sparse arrays would be maintaining non-sparse array for sparse keys for..in with that array then using "numeric string" object properties to get / set actual values? I don't know, the speed is not so critical in my usage case.
- Sparse objects don't exist in any language afaik. What you created
is
just a regular plain object.
I was mentioning JS objects with numeric keys - I didn't know they are silently converted to numbers. Now I removed parseInt() from my code.
Mistyped, silently converted to strings, of course.
MediaWiki with extensions does a lot of requests, which requests do load extension messages? I'll try to find. Dmitriy
On Thu, 22 Mar 2012 00:15:40 -0700, Dmitriy Sintsov questpc@rambler.ru wrote:
- Krinkle krinklemail@gmail.com [Wed, 21 Mar 2012 18:51:45 +0100]:
Few points:
- The easiest way to understand it is to consider "sparse arrays" to
not
exist in javascript. It doesn't throw an exception if you try it, but it's not supposed
to
be posisble, so don't.
In Chrome, executing the following code: var a = []; a[0] = 'a'; a[2] = 'c'; worked, however debugger inspector shows 'undefined' elements between real elements of array. And the length is counted for 0..last element, including the undefs. However, for..in worked for such arrays fine, but may be inefficient - I do not think it has hashes or linked lists to skip the undefs, it probably just skips them one by one. That worked in IE8/IE9/FF/Chrome so I must not be first coder who used that.
That's the very definition of an array. An array is a list, the keys are indexes. By definition you cannot have an index that does not exist. If an item in the array does not exist then the items after it take on earlier indexes and the length is shorter. If you have a case where 0 and 2 exists but 1 is not supposed to exist then you're not using an array, you're using a hashtable, map, etc... For that purpose you should be using Object like a hashtable: var h = {} h[0] = 'a'; h[2] = 'c';
You can use for..in on such a thing. And you also shouldn't have to worry about using hasOwnProperty on it. If anyone is stupid enough to set something on Object.prototype a lot more than your script will break.
I switches to {} and $.each() two days ago, so that's already the past.
- Sparse objects don't exist in any language afaik. What you created
is
just a regular plain object.
I was mentioning JS objects with numeric keys - I didn't know they are silently converted to numbers. Now I removed parseInt() from my code.
- As I said before I think there is no point in using $.each() in this case. It is nice if you need a context but as you already found out,
more often than not it's actually a burden to have to deal with it if you just want a loop inside an existing context. I would've kept the for-in loop you originally had which was just fine, only change the array into an object.
Maybe I'll switch to for ...in back then however I scared by "basic" prototypes so maybe I'll check .hasOwnProperty() then, too. Still haven't decided that.
I'd have to look further into the actual code to know what you mean here, but there isn't really such thing as "refactorign a JS class into a ResourceLoader module". For one, javascript doesn't have classes, but that's just word choise. Secondly, ResourceLoader "just" loads
Actually, creating JS function and assigning the prototypes to it and then instantiating it via new operator and then adding properties to it's context, including some from it's own prototypes works like classes. Prototypes are really similar to virtual methods tables available in another OO languages, but another languages manipulate them indirectly, in higher and better level than JS. Frankly, I am not much a fan of JS, however I have no another widespread choice for client-programming web.
You don't have to refactor anything. There are a few things that it enforces (such as execution of the javascript file in a local
context),
but other than that you don't have to change anything "for
ResourceLoader".
There are a lot of conventions and best practices often associated
with
"porting to ResourceLoader" but that's more along the lines of "now
that
we're working on this, lets get rid of crappy code and make it nice", not anything ResourceLoader actually requires.
I'm curious what kind of problems you are referring to here? The kind
of
problems you didn't have when you loaded jQuery in MediaWiki 1.15
The problems are simple - when I load another module with mw.loader.using, then call it's module from mw.loader.using callback, there is no return to original module. So I am enforced to build inflexible chain of calling main module directly from loaded module main context. I got used to it, although that was a little disappointment.
Now I have another pain: mw.msg() caches messages quite aggressively, even though I put the following lines in LocalSettings.php: $wgResourceLoaderDebug = true; $wgEnableParserCache = false; $wgCachePages = false;
I got the same old version of message, even after multiple Ctrl+R in Chrome, action=purge and even httpd restats. How can I make mw.msg() being updated every time I change i18n file of my extension? Maybe the evil provider caches some requests though :-( I probably should check with Wireshark. It's tiresome that fighting with environment takes too much of the time. Dmitriy
* Daniel Friesen lists@nadir-seen-fire.com [Thu, 22 Mar 2012 01:40:16 -0700]:
That's the very definition of an array. An array is a list, the keys
are
indexes. By definition you cannot have an index that does not exist.
If PHP arrays are sparse, including numeric ones. They are actually like hashmaps I think so. I don't know whether they use different ways to store numeric and string keys. I haven't studied the low-level implementation. They also have "real" arrays like in C (lower level), however these are the part of SPL: http://php.net/manual/en/class.splfixedarray.php
an item in the array does not exist then the items after it take on
earlier
indexes and the length is shorter. If you have a case where 0 and 2 exists but 1 is not supposed to exist then you're not using an array, you're using a hashtable, map, etc... For that purpose you should be using Object like a hashtable: var h = {} h[0] = 'a'; h[2] = 'c';
You can use for..in on such a thing. And you also shouldn't have to worry about using hasOwnProperty on it. If anyone is stupid enough to set something on Object.prototype a lot more than your script will break.
Ok. Which are the cases when jQuery.each() is preferable? I use it with jQuery selectors. Or, something else as well? Dmitriy
On Thu, 22 Mar 2012 03:22:10 -0700, Dmitriy Sintsov questpc@rambler.ru wrote:
- Daniel Friesen lists@nadir-seen-fire.com [Thu, 22 Mar 2012 01:40:16
-0700]:
That's the very definition of an array. An array is a list, the keys
are
indexes. By definition you cannot have an index that does not exist.
If PHP arrays are sparse, including numeric ones. They are actually like hashmaps I think so. I don't know whether they use different ways to store numeric and string keys. I haven't studied the low-level implementation. They also have "real" arrays like in C (lower level), however these are the part of SPL: http://php.net/manual/en/class.splfixedarray.php
PHP arrays are not arrays at all. PHP's array() is a bastardized fusion of arrays and hash tables. But they insist on calling them arrays despite the hard fact that they are completely different from arrays in every other language.
PHP's implementation of an array is in zend_hash.c it's a HashTable with ordered keys. I think I see some linked-list like code inside there that handles the ordering.
When you use $x[0] in an array you are really looking up the value for the key "0". Hence you can actually have an "array" where 3 comes before 1. There's no real way to access the actual array indexes of a php 'array' besides when you use things like reset, next, array_splice, etc...
an item in the array does not exist then the items after it take on
earlier
indexes and the length is shorter. If you have a case where 0 and 2 exists but 1 is not supposed to exist then you're not using an array, you're using a hashtable, map, etc... For that purpose you should be using Object like a hashtable: var h = {} h[0] = 'a'; h[2] = 'c';
You can use for..in on such a thing. And you also shouldn't have to worry about using hasOwnProperty on it. If anyone is stupid enough to set something on Object.prototype a lot more than your script will break.
Ok. Which are the cases when jQuery.each() is preferable? I use it with jQuery selectors. Or, something else as well? Dmitriy
$.each is useful for arrays when you don't want to write a full for ( var i ... blah blah or need a function scope to make closures work.
$.each is only useful for objects if you want a function scope for closures.
On 19.03.2012 17:23, Krinkle wrote:
<offtopic>
On Mon, Mar 19, 2012 at 9:35 AM, Daniel Friesen lists@nadir-seen-fire.comwrote:
On Mon, 19 Mar 2012 00:40:54 -0700, Dmitriy Sintsovquestpc@rambler.ru wrote: var jqgmap = [];
for ( var mapIndex in jqgmap ) {
This is VERY bad JavaScript coding practice. Please use $.each().
This is rather exaggerated. Even more when looking at that suggestion.
Arrays should, indeed, not be enumerated with a for-in loop. Arrays in JS can only contain numeral indices, so they should simply be iterated with a simple for-loop like this `for (i = 0; i< myArr.length; i += 1) { .. }` (maybe cache length for slight performance gain by reducing property lookups).
My array is numeric but sparse, myArr = []; myArr[0] = a; myArr[1] = b; So I cannot just use incremental key iteration, at least existence of element should be checked.
Using $.each has overhead (+1+n function invocations). When given an array it will do a simple for-loop with a counter. It has overhead of 1+n additional function invocations and context creations. In most cases there is no use for it. There is one case where it is handy and that's when you specifically need a local context for the loop (being careful not to create later-called functions inside the loop, risking all variables being in the post-loop state). If you don't need a local scope for your loop, then using $.each (or the native [].forEach in later browsers) is pointless as it only has overhead of additional function invocations and lowering the position in the scope chain.
When iterating over objects, however, (not arrays) then $.each is no better than a for-in loop because (contrary to what some people think) it is not a shortcut for for-in + if-hasOwn wrapper. When an object is passed, it literally just does a plain for-in loop invoking the callback with the value. jQuery does not support environments where someone extends the native Object.prototype because it is considered harmful (an therefore MediaWiki inherently does not support that either), so a plain for-in loop over an object (excluding array objects) is perfectly fine according to our conventions.
See also http://stackoverflow.com/a/1198447/319266
.... but so much for the good (and bad, evil) parts of javascript :D
I was thinking about Daniel warnings on Array prototyping and thought that proper iteration would check whether the prototype has the same member, then do not include it (skip from) callback. Dmitriy
On Mon, 19 Mar 2012 06:23:13 -0700, Krinkle krinklemail@gmail.com wrote:
<offtopic>
On Mon, Mar 19, 2012 at 9:35 AM, Daniel Friesen lists@nadir-seen-fire.comwrote:
On Mon, 19 Mar 2012 00:40:54 -0700, Dmitriy Sintsov questpc@rambler.ru wrote: var jqgmap = [];
for ( var mapIndex in jqgmap ) {
This is VERY bad JavaScript coding practice. Please use $.each().
This is rather exaggerated. Even more when looking at that suggestion.
There's nothing exaggerated about telling people not to use for..in to loop over arrays. for..in is object iteration, period.
The $.each recommendation is because that IS our equivalent to foreach for arrays. Don't underestimate the developer distaste for writing stuff like this all the time: `for ( var i = 0, l = arr.length; i < l; i++ ) { var arr2 = arr[i]; for ( var j = 0, ll = arr2.length; j < ll; j++ ) { var item = arr2[j]; } }`
I never said not to use numeric for loops. But there's no reason to ditch clean code.
Arrays should, indeed, not be enumerated with a for-in loop. Arrays in JS can only contain numeral indices, so they should simply be iterated with a simple for-loop like this `for (i = 0; i < myArr.length; i += 1) { .. }` (maybe cache length for slight performance gain by reducing property lookups).
Using $.each has overhead (+1+n function invocations). When given an array it will do a simple for-loop with a counter. It has overhead of 1+n additional function invocations and context creations. In most cases there is no use for it. There is one case where it is handy and that's when you specifically need a local context for the loop (being careful not to create later-called functions inside the loop, risking all variables being in the post-loop state). If you don't need a local scope for your loop, then using $.each (or the native [].forEach in later browsers) is pointless as it only has overhead of additional function invocations and lowering the position in the scope chain.
When iterating over objects, however, (not arrays) then $.each is no better than a for-in loop because (contrary to what some people think) it is not a shortcut for for-in + if-hasOwn wrapper. When an object is passed, it literally just does a plain for-in loop invoking the callback with the value. jQuery does not support environments where someone extends the native Object.prototype because it is considered harmful (an therefore MediaWiki inherently does not support that either), so a plain for-in loop over an object (excluding array objects) is perfectly fine according to our conventions.
See also http://stackoverflow.com/a/1198447/319266
.... but so much for the good (and bad, evil) parts of javascript :D
-- Krinkle
Micro-optimizations like these are pointless. Most arrays have under 100 elements. No matter what type of loop you use the time taken for the operation is negligible. More important is that the code is clean and easy to read and write.
Unless you are working with 5000+ element arrays or working with really low level code doing things like parsing and highlighting code there's no real reason to micro-optimize.
If we cared about efficiency at that level. We wouldn't be using any of jQuery's dom querying or dom creation functionality. dom operations are practically the slowest thing there is to work with and jQuery just adds overhead.
Btw, ;) for..in is actually the least efficient way to loop, the only thing that is slower than for..in is for..in with hasOwnProperty: http://jsperf.com/loop-testx http://jsperf.com/loop-test-short
wikitech-l@lists.wikimedia.org