Preloading of Javascript Files - javascript

Working on a small engine to run my HTML5 test games, using it as a great way to get deeper into Javascript and have fun at the same time. Succeeding, btw.
However, I just found this cool little script called PreloadJS ( http://www.createjs.com/#!/PreloadJS ). Also using John Resig's classical inheritence class JS. Very cool, enjoying it. I am attempting to use PreloadJS to pull in all of my engine files...but I seem to be having an issue. Here's the code I'm using (keeping it simple on purpose):
var ScriptLoader = Class.extend({ // Want to add functionality to this to allow PHP or inline loading...perhaps later
init: function() {
this.fileList = [
'Empty.js',
'./engine/Scene.js'
];
this.preload;
},
loadProjectSource: function(directory) {
if (this.preload != null) { this.preload.close(); }
this.preload = new createjs.LoadQueue();
this.preload.addEventListener("fileload", this.fileLoaded);
this.preload.addEventListener("error", this.fileError);
this.preload.setMaxConnections(5);
this.loadAnother();
},
loadAnother: function() {
var item = this.fileList.shift();
if(this.fileList.length != 0) {
this.preload.loadFile(item);
}
},
fileLoaded: function(e) {
debug('Loaded ' + e.item.src);
this.loadAnother();
},
fileError: function(e) {
debug('Error ' + e.item.src);
}
}
From my engine instantiation, I'm calling ScriptLoader.loadProjectSource. It's doing nothing but throwing errors, and the documentation on error handling (and loading JS files in general...) is very sparse on the PreloadJS site. It focuses on preloading images (which, admittedly, looks great). Anyway, so yea, it's throwing errors. And it can't be the files, as I tried loading a completely blank JS file (as you can see). Still throwing an error on the Empty.js file.
Annoyed :) Thanks in advance.

The PreloadJS script uses XHR where available with browser support. In order for this to function correctly with locally-hosted scripts, a local webserver must be running. Upon activating my local webserver and attempting the same operation, full success.

Related

Can content be updated via Ajax to end user without loosing scroll position from update/refresh?

This question is being asked to check if it is POSSIBLE to do what I’d like it to do, not so much as a HOW to do it question (although if someone wants to pen something up is always good too). As I am more in the infancy of my web-dev learning I figured it prudent to determine if the hopeful result is even possible before investing the substantial time into it to achieve it. I have read conflicting things from various references on the web and figured this is the place to ask others' opinions to be sure one way or the other.
I’m running Drupal 8 (8.9.13 at time of writing). The page uses Views. To keep it simple the page has 3 sections; a top, middle, and bottom (this is the page/content itself, this is NOT a header, body, footer). The bottom section/View consists of multiple div’s/sections. The content of all 3 sections is primarily links.
I’m using/trying the module ‘Views Auto-Refresh D8’. After install and setup it updates the page perfectly EXCEPT that not only after a content update, but ALSO after each Ajax check/interval the page returns to the top of the page, causing the user experience to be unacceptable. In other words, if reading the bottom of the page, once the page automatically checks for updates via Ajax the page suddenly returns to top of page.
Researching this I got the impression that is normal behavior for Ajax. I also got the impression that this behavior can be overridden, however, other sources made it sound as though it can’t. Hence the purpose of this question here.
The Javascript for the ‘Views Auto-Refresh D8’ module looks like this:
(function ($, Drupal, drupalSettings) {
// START jQuery
Drupal.behaviors.views_autorefresh = {
attach: function(context, settings) {
for(var view_name in settings.views_autorefresh) {
for(var view_display in settings.views_autorefresh[view_name]) {
var interval = settings.views_autorefresh[view_name][view_display];
var execution_setting = '.view-'+view_name.replace(new RegExp('_','g'),'-')+'.view-display-id-'+view_display;
if($(context).find(execution_setting).once(execution_setting).length > 0) {
// Delete timeOut before reset it
if(settings.views_autorefresh[view_name][view_display].timer) {
clearTimeout(settings.views_autorefresh[view_name][view_display].timer);
}
settings.views_autorefresh[view_name][view_display].timer = setInterval(
function() {
Drupal.behaviors.views_autorefresh.refresh(execution_setting)
}, interval
);
}
}
}
},
refresh: function(execution_setting) {
$(execution_setting).trigger('RefreshView');
}
}
Through researching this question I found closely related questions saying the page refresh can be bypassed/prevented by adding a javascript function such as:
preventDefault() / event.preventDefault()
However I’m not entirely sure if this event method can be used to achieve the result I’m pursuing? Or if the following is a better avenue to explore?
The other possible workaround/idea I came up with and am curious about is that I use a module called ‘Geysir’ that allows me to update my content directly on the page from the front-end. The content can be moved, deleted directly on the page, or even modified within a modal overlay that then updates the content without the page refresh/scroll back to top that the ‘Views Auto-Refresh D8’ is doing. I’m wondering if the logic from the Geysir module can be applied to the Views Auto-Refresh D8 module? With my moderate javascript knowledge it looks like the relevant js/Ajax logic in the Geysir module is:
$.each(Drupal.ajax.instances, function (index, event) {
var element = $(event.element);
if (element.hasClass('geysir-paste')) {
if (href === event.element_settings.url) {
event.options.url = event.options.url.replace('/' + paragraph_id + '/', '/' + parent_id + '/');
}
}
});
});
});
return false;
});
}
};
Drupal.AjaxCommands.prototype.geysirReattachBehaviors = function() {
Drupal.ajax.instances = Drupal.ajax.instances.filter(function(el) {
return el;
});
Drupal.attachBehaviors();
};
})(jQuery, Drupal, drupalSettings);
(lines 40-64)
That’s pretty much where I’m at before moving forward. So:
Is it even possible to maintain end-users scroll/page position while the Ajax call is being made and when content is updated via Ajax?
and if so,
Which avenue above is best to pursue, OR, is there yet another avenue I haven’t yet run across yet?

How can I access the DOM of a <webview> in Electron?

I'm just getting started with Electron, with prior experience with node-webkit (nw.js).
In nw.js, I was able to create iframes and then access the DOM of said iframe in order to grab things like the title, favicon, &c. When I picked up Electron a few days ago to port my nw.js app to it, I saw advice to use webviews instead of iframes, simply because they were better. Now, the functionality I mentioned above was relatively easy to do in nw.js, but I don't know how to do it in Electron (and examples are slim to none). Can anyone help?
Also, I have back/forward buttons for my webview (and I intend on having more than one). I saw in the documentation that I could call functions for doing so on a webview, but nothing I have tried worked either (and, I haven't found examples of them being used in the wild).
I dunno who voted to close my question, but I'm glad it didn't go through. Other people have this question elsewhere online too. I also explained what I wanted to achieve, but w/e.
I ended up using ipc-message. The documentation could use more examples/explanations for the layperson, but hey, I figured it out. My code is here and here, but I will also post examples below should my code disappear for whatever reason.
This code is in aries.js, and this file is included in the main renderer page, which is index.html.
var ipc = require("ipc");
var webview = document.getElementsByClassName("tabs-pane active")[0];
webview.addEventListener("ipc-message", function (e) {
if (e.channel === "window-data") {
// console.log(e.args[0]);
$(".tab.active .tab-favicon").attr("src", e.args[0].favicon);
$(".tab.active .tab-title").html(e.args[0].title);
$("#url-bar").val(e.args[0].url);
$("#aries-titlebar h1").html("Aries | " + e.args[0].title);
}
// TODO
// Make this better...cancel out setTimeout?
var timer;
if (e.channel === "mouseover-href") {
// console.log(e.args[0]);
$(".linker").html(e.args[0]).stop().addClass("active");
clearTimeout(timer);
timer = setTimeout(function () {
$(".linker").stop().removeClass("active");
}, 1500);
}
});
This next bit of code is in browser.js, and this file gets injected into my <webview>.
var ipc = require("ipc");
document.addEventListener("mouseover", function (e) {
var hoveredEl = e.target;
if (hoveredEl.tagName !== "A") {
return;
}
ipc.sendToHost("mouseover-href", hoveredEl.href);
});
document.addEventListener("DOMContentLoaded", function () {
var data = {
"title": document.title,
"url": window.location.href,
// need to make my own version, can't rely on Google forever
// maybe have this URL fetcher hosted on hikar.io?
"favicon": "https://www.google.com/s2/favicons?domain=" + window.location.href
};
ipc.sendToHost("window-data", data);
});
I haven't found a reliable way to inject jQuery into <webview>s, and I probably shouldn't because the page I would be injecting might already have it (in case you're wondering why my main code is jQuery, but there's also regular JavaScript).
Besides guest to host IPC calls as NetOperatorWibby, it is also very useful to go from host to guest. The only way to do this at present is to use the <webview>.executeJavaScript(code, userGesture). This api is a bit crude but it works.
If you are working with a remote guest, like "extending" a third party web page, you can also utilize webview preload attribute which executes your custom script before any other scripts are run on the page. Just note that the preload api, for security reasons, will nuke any functions that are created in the root namespace of your custom JS file when your custom script finishes, however this custodial process will not nuke any objects you declare in the root. So if you want your custom functions to persist, bundle them into a singleton object and your custom APIs will persist after the page fully loads.
[update] Here is a simple example that I just finished writing: Electron-Webview-Host-to-Guest-RPC-Sample
This relates to previous answer (I am not allowed to comment): Important info regarding ipc module for users of Electron 1.x:
The ipc module was split into two separate modules:
ipcMain for the main process
ipcRenderer for the renderer process
So, the above examples need to be corrected, instead of
// Outdated - doesn't work in 1.x
var ipc = require("ipc");
use:
// In main process.
var ipcMain = require('electron').ipcMain
And:
// In renderer process.
var ipcRenderer = require('electron').ipcRenderer
See: http://electron.atom.io/blog/2015/11/17/electron-api-changes section on 'Splitting the ipc module'

Moving created files with JXA

I'm new to JXA scripting, but I'm attempting to troubleshoot some older scripts currently in place here at work. They loop through an InDesign document and create several PDFs based on it. Previously, they would be stored in a folder called "~/PDFExports". However, this doesn't work with 10.10.
If I change the code to just place the PDFs in "~/", it works fine. From there, I'd like to move the files to "~/PDFExports", but I can't seem to find an answer on how to do that. I've seen things about making calls to ObjC, or to call Application('Finder'), but neither work - they both return undefined.
Am I just missing something basic here, or is it really this hard to simply move a file with JXA?
EDIT: Some syntax for how I'm creating the folder in question and how I'm attempting to work with Finder.
//This is called in the Main function of the script, on first run.
var exportFolder = new Folder(exportPath);
if(!exportFolder.exists) {
exportFolder.create();
}
//This is called right after the PDF is created. file is a reference to the
actual PDF file, and destination is a file path string.
function MoveFile(file,destination){
var Finder = Application("Finder");
Application('Finder').move(sourceFile, { to: destinationFolder });
alert("File moved");
}
Adobe apps have long included their own embedded JS interpreter, JS API, and .jsx filename extension. It has nothing to do with JXA, and is not compatible with it.
InDesign's JSX documentation:
http://www.adobe.com/devnet/indesign/documentation.html#idscripting
(BTW, I'd also strongly advise against using JXA for Adobe app automation as it has a lot of missing/broken features and application compatibility problems, and really isn't fit for production work.)
Here's the link to Adobe's InDesign Scripting forum, which is the best place to get help with JSX:
https://forums.adobe.com/community/indesign/indesign_scripting
You could use Cocoa to create the folder
var exportFolder = $.NSHomeDirectory().stringByAppendingPathComponent("PDFExports")
var fileManager = $.NSFileManager.defaultManager
var folderExists = fileManager.fileExistsAtPath(exportFolder)
if (!folderExists) {
fileManager.createDirectoryAtPathWithIntermediateDirectoriesAttributesError(exportFolder, false, $(), $())
}
and to move a file
var success = fileManager.moveItemAtPathToPathError(sourceFile, destinationLocation, $());
if (success) alert("File moved");
Consider that destinationLocation must be the full path including the file name
and both sourceFile and destinationLocation must be NSString objects like exportFolder
Could it be that the folder is missing ? Could be your reference to the folder object not valid ? Any syntax to show ?
I will share some of what I learned about JXA move and duplicate methods. I am not a professional programmer just an attorney that is passionate about automation. My comments come from much trial and error, reading whatever I could find online, and A LOT of struggle. The move method does not work well with Finder. Use the System Events move method instead. The duplicate method in Finder works just fine. The duplicate method does not work well in system events. This is a modified snippet from a script I wrote showing move() using System Events.
(() => {
const strPathTargetFile = '/Users/bretfarve/Documents/MyFolderA/myFile.txt';
const strPathFolder = '/Users/bretfarve/Documents/MyFolderB/';
/* System Events Objects */
const SysEvents = Application('System Events');
const objPathFolder = SysEvents.aliases[strPathFolder];
SysEvents.move(SysEvents.aliases.byName(strPathTargetFile), {to: objPathFolder});
})();

Uncaught TypeError: $player.jPlayer is not a function

I'm helping a friend with his site and after updating his WordPress installation to address the recent security issue, the JPlayer plugin that was handling audio on his site stopped working.
Chrome's console shows the error in the title, but I don't know JS well enough to be able to debug it properly. I'm pretty sure that the plugin itself is loaded correctly, along with JQuery, in the page header. I checked it against the plugin's instructions and it all appears fine.
I've also updated the plugin itself to ensure that it's not some compatibility issue.
I did not build his site, nor am I familiar with this particular plugin at all, I'm just trying to see if it's an easy fix or if I have to restore a backup.
I assume it has something to do with how his web designer (they had a falling out) implemented it in the main.js file, but that's about as far as I've gotten.
Help?
Really condensing and removing parts of main.js, it looks like
var $player = false,
$(document).ready(function() {
if(!$player) {
$("#jPlayer").jPlayer({
ready: function() {
$player = $(this); // IT'S BEING SET HERE !
PlaylistPlay(playlistObject,trackIndex);
}
});
} else {
PlaylistPlay(playlistObject,trackIndex);
}
});
function PlaylistPlay(lePID,trackIndex) {
playTrack(trackIndex);
}
function playTrack(index) {
$player.jPlayer("setMedia", {mp3: trackObject.mp3,oga: trackObject.oga}).jPlayer("play");
}
If you look closely at that, you'll see that there is a distinct possibility that PlaylistPlay can be called without $player being set to $(this), it's actually almost a certaintity, which means that $player is false, and doing
false.jPlayer(...
doesn't really work, see the console output that confirms the variable is false
The plugin is not initializing correctly. On $(document).ready() it's trying to initialize the plugin and it's reporting a Flash error.
Here's the significant part of the code:
$("#jPlayer").jPlayer({
...
error: function(event) {
var out = "<p id=\"noSolution\">Sorry, you need an HTML5 capable browser or flash to be able to listen to music on this website.<br /> Reason: ";
switch(event.jPlayer.error.type) {
case $.jPlayer.error.FLASH:
out += "A problem with the Flash insertion on the page.";
break;
...
}
}
...
});
Digging a bit deeper, I can trace this back to the vimeo.jplayer in the specific code block:
_flash_volume: function(a) {
try {
this._getMovie().fl_volume(a)
} catch (b) {
this._flashError(b)
}
}
That function is throwing an exception because this._getMovie() does not have a property named fl_volume.
The error you actually see is a side-effect of this failure. You could try removing the line: this._flashError(b) from the above statement and see if the error can be safely ignored.

Auto-load/include for JavaScript

I have file called common.js and it's included in each page of my site using <script />.
It will grow fast as my sites functionality will grow (I hope; I imagine). :)
Lets example I have a jQuery event:
$('#that').click(function() {
one_of_many_functions($(this));
}
For the moment, I have that one_of_many_functions() in common.js.
Is it somehow possible that JavaScript automatically loads file one_of_many_functions.js when such function is called, but it doesn't exist? Like auto-loader. :)
The second option I see is to do something like:
$('#that').click(function() {
include('one_of_many_functions');
one_of_many_functions($(this));
}
That not so automatically, but still - includes wanted file.
Is any of this possible? Thanks in an advice! :)
It is not possible to directly auto-load external javascripts on demand. It is, however, possible to implement a dynamic inclusion mechanism similar to the second route you mentioned.
There are some challenges though. When you "include" a new external script, you aren't going to be able to immediately use the included functionality, you'll have to wait until the script loads. This means that you'll have to fragment your code somewhat, which means that you'll have to make some decisions about what should just be included in the core vs. what can be included on demand.
You'll need to set up a central object that keeps track of which assets are already loaded. Here's a quick mockup of that:
var assets = {
assets: {},
include: function (asset_name, callback) {
if (typeof callback != 'function')
callback = function () { return false; };
if (typeof this.assets[asset_name] != 'undefined' )
return callback();
var html_doc = document.getElementsByTagName('head')[0];
var st = document.createElement('script');
st.setAttribute('language', 'javascript');
st.setAttribute('type', 'text/javascript');
st.setAttribute('src', asset_name);
st.onload = function () { assets._script_loaded(asset_name, callback); };
html_doc.appendChild(st);
},
_script_loaded: function (asset_name, callback) {
this.assets[asset_name] = true;
callback();
}
};
assets.inlude('myfile.js', function () {
/* do stuff that depends on myfile.js */
});
Sure it's possible -- but this can become painful to manage. In order to implement something like this, you're going to have to maintain an index of functions and their corresponding source file. As your project grows, this can be troublesome for a few reasons -- the 2 that stick out in my mind are:
A) You have the added responsibility of maintaining your index object/lookup mechanism so that your scripts know where to look when the function you're calling cannot be found.
B) This is one more thing that can go wrong when debugging your growing project.
I'm sure that someone else will mention this by the time I'm finished writing this, but your time would probably be better spent figuring out how to combine all of your code into a single .js file. The benefits to doing so are well-documented.
I have created something close to that a year ago. In fact, I have found this thread by search if that is something new on the field. You can see what I have created here: https://github.com/thiagomata/CanvasBox/blob/master/src/main/New.js
My project are, almost 100% OOP. So, I used this fact to focus my solution. I create this "Class" with the name "New" what is used to, first load and after instance the objects.
Here a example of someone using it:
var objSquare = New.Square(); // Square is loaded and after that instance is created
objSquare.x = objBox.width / 2;
objSquare.y = objBox.height / 2;
var objSomeExample = New.Stuff("some parameters can be sent too");
In this version I am not using some json with all js file position. The mapping is hardcore as you can see here:
New.prototype.arrMap = {
CanvasBox: "" + window.MAIN_PATH + "CanvasBox",
CanvasBoxBehavior: "" + window.MAIN_PATH + "CanvasBoxBehavior",
CanvasBoxButton: "" + window.MAIN_PATH + "CanvasBoxButton",
// (...)
};
But make this more automatic, using gulp or grunt is something what I am thinking to do, and it is not that hard.
This solution was created to be used into the project. So, the code may need some changes to be able to be used into any project. But may be a start.
Hope this helps.
As I said before, this still is a working progress. But I have created a more independent module what use gulp to keep it updated.
All the magic que be found in this links:
https://github.com/thiagomata/CanvasBox/blob/master/src/coffee/main/Instance.coffee
https://github.com/thiagomata/CanvasBox/blob/master/src/node/scripts.js
https://github.com/thiagomata/CanvasBox/blob/master/gulpfile.js
A special look should be in this lines of the Instance.coffee
###
# Create an instance of the object passing the argument
###
instaceObject = (->
ClassElement = (args) ->
window[args["0"]].apply this, args["1"]
->
ClassElement:: = (window[arguments["0"]])::
objElement = new ClassElement(arguments)
return objElement
)()
This lines allows me to initialize a instance of some object after load its file. As is used in the create method:
create:()->
#load()
return instaceObject(#packageName, arguments)

Categories

Resources