create a template url-routing script - javascript
I want to create an url-routing script using javascript as much as possible, but also accepting jQuery in the code. The js file has to change the url path (although I used location.hash instead of location.pathname) and the content of a div with the view id (from external files) accordingly.
Example configuration:
root/index.html
root/tpl/home.html
root/tpl/about.html
home.html content:
<p>This is content of home page</p>
about.html content:
<p>This is the content of the about page </p>
What I have done so far:
'use strict';
var Router = {
root: '/',
routes: [],
urls: [],
titles: [],
navigate: function() {
location.hash = this.root;
return this;
},
add: function(thePath, theUrl, theTitle) {
this.routes.push(thePath);
this.urls.push(theUrl);
this.titles.push(theTitle);
},
loading: function() {
this.navigate();
var r = this.routes;
var u = this.urls;
window.onload = function() {
$("#view").load("tpl/home.html");
};
window.onhashchange = function() {
for (var i = 0; i < r.length; i++) {
if (location.hash == r[i]) {
$("#view").load(u[i]);
}
}
};
}
};
Router.add("#/home", "tpl/home.html", "Home Page");
Router.add("#/about", "tpl/about.html", "About Page");
Router.loading();
Desired type of url:
http://mywebsite.com/
http://mywebsite.com/about
I know there are more than enough libraries that make the routing, like AngularJS and Crossroad, I want to know how this could be done.
To make this URL work - http://mywebsite.com/about - you need a server that knows how to route this request. Since the actual file name is about.html your server must know what to do with extensionless URLs.
Usually, the server uses the file extension as a clue for how to serve up content. For example, if it sees file.php it knows to use the PHP component, for .aspx it knows to use the ASP.NET component, and for .htm or .html it knows to respond with plain HTML (and usually serves the file instead of processing it). Your server must have some rules for what to do with any request, whether it has an extension or not, but without an extension you need to provide an explicit routing rule for that request..
The capabilities for JavaScript to do routing are limited because it requires the user to already be on your site. You can do some interesting things if you parse the URL parameters or use hashes and use them for routing, but that still requires requesting a page from your site as the first step.
For example: the server is already doing some level of "extensionless routing" when you give it this request:
http://mywebsite.com/
The parts of the URL are:
http - protocol
(port 80 is implied because it is default HTTP port)
mywebsite.com - domain AKA host
/ the path
The server sees / and uses what IIS calls a "default document" (I think apache calls it "default index" or "default page"). The server has been configured to return a file such as "index.html" or "default.htm" in this case. So when you request http://mywebsite.com/ you actually may get back the equivalent of http://mywebsite.com/index.html
When the server sees http://mywebsite.com/about it may first look for a folder named about and next for a file named about, but since your file is actually named about.html and is in a different folder (/tpl) the server needs some help to know how to translate http://mywebsite.com/about into the appropriate request - which for you would be http://mywebsite.com/#/about so that it requests the routing page (assuming it is the default document in the web app root folder) so that the browser can parse and execute the JavaScript that does the routing. Capisce?
You might be interested by frontexpress.
My library fix your case like below:
// Front-end application
const app = frontexpress();
const homeMiddleware = (req, res) => {
document.querySelector('#view').innerHTML = '<p>This is content of home page</p>';
}
app.get('/', homeMiddleware);
app.get('/home', homeMiddleware);
app.get('/about', (req, res) => {
document.querySelector('#view').innerHTML = '<p>This is the content of the about page </p>';
});
Obviously, you can get the template files from the server.
The #view will be feeded as below:
document.querySelector('#view').innerHTML = res.responseText;
More detailed sample in this gist
I have worked with what your answers and I have build the following Router. The only issue remains that it still uses location.hash
(function() {
var Router = {
root: '#/',
routes: [],
urls: [],
titles: [],
add: function(thePath, theUrl, theTitle) {
this.routes.push(thePath);
this.urls.push(theUrl);
this.titles.push(theTitle);
},
navigate: function() {
var routes = this.routes,
urls = this.urls,
root = this.root;
function loading() {
var a = $.inArray(location.hash, routes),
template = urls[a];
if (a === -1) {
location.hash = root;
$("#view").load(urls[0]);
}
else {
$("#view").load(template);
if (a === 0) {
window.scrollTo(0, 0);
}
else {
window.scrollTo(0, 90);
}
}
}
window.onload = loading;
window.onhashchange = loading;
}
};
Router.add("#/", "tpl/home.html", "Home Page");
Router.add("#/about", "tpl/about.html", "About Page");
Router.add("#/licence", "tpl/licence.html", "MIIT Licence");
Router.add("#/cabala", "tpl/cabala.html", "Cabala Checker");
Router.add("#/articles/esp", "tpl/article1.html", "ESP");
Router.add("#/fanfics/the-chupacabra-case", "tpl/article2.html", "The Chupacabra Case");
Router.navigate();
})();
Your request reads like what you're attempting to do is to add a "path+file" ("/tpl/about.html") to a base url ("www.myhost.com"). If that's the case, then you need to dynamically extract the host name from your current document and then append the new URL to the existing base. You can get the existing host name by executing the following commands in javascript:
var _location = document.location.toString();
var serverNameIndex = _location.indexOf('/', _location.indexOf('://') + 3);
var serverName = _location.substring(0, serverNameIndex) + '/';
This will return a string like: "http://www.myhost.com/" to which you can now append your new URL. All of this can be done in javascript prior to sending to the server.
If you only want the server name, without the leading http or https, then change the last line to be:
var serverName = _location.substring(_location.indexOf('://') + 3, serverNameIndex) + '/';
Lets break your code down a little bit:
function loading() {
"location.hash" is set based on the URL most recently clicked (www.myhost.home/#about). One essential question is, is this the action that you want, or do you want to pass in a value from the html for the onClick operation? It seems like that would be a more effective approach for you.
var a = $.inArray(location.hash, routes),
template = urls[a];
var a will be set to either -1 or the location of "location.hash" in the "routes array. If location.hash does not exist in routes, then a==-1 and the script will fail, because you're setting template = urls[-1]. You may want to move setting template to inside the "else" statement.
if (a === -1) {
location.hash = root;
$("#view").load(urls[0]);
}
else {yada yada yada
}
You could use a sequence in your html analogous to:
<a onclick="loading('#about')">Go to About Page</a>
Related
How to res.sendFile whilst res.rendering an HTML that uses the sent file in Express.js?
I am trying to create a file server that use node.js and Express. I have the system currently set up so that each user has a file directory. I currently have it set so that the whole file system is hosted using app.use(express.static(testFolder)); The problem with this approach is that anyone could view anyones files even if they are not logged in. I have looked and other people recommend to not host the whole file system and use res.sendFile(specific file) instead. People also recommend to use middleware to check that the user is authenticated. My problem with this is that, say for photos, I have a photos route and EJS file set up so that for all photos, the photo that is to be viewed is rendered inside the .ejs file. How would I res.sendFile() but then have it rendered inside a .ejs file? As just res.sendFile() only shows the photo with a white background and no customisation. Here is the code for the route: router.post("/view/photo", function(req, res){ var path = req.body.currentPath var filename = req.body.fileName var currentUser = req.body.currentUser var files = fs.readdirSync(path) var images = [] var fileExtensions = ['jpg', 'jpeg', 'gif', 'png'] for(var i = 0; i < files.length; i++){ fileExtension = files[i].split(".")[1] if(fileExtension){ if(fileExtensions.includes(fileExtension.toLowerCase())){ images.push(files[i]) } } } var indexOfFile = images.indexOf(filename) var next = indexOfFile + 1; var previous = indexOfFile - 1; if(next > images.length - 1){ next = next - (images.length) } if(previous < 0){ previous = previous + (images.length) } path = path.split("/") path = path.splice(path.indexOf(currentUser, 0), path.length) path = path.join("/") finalPath = "../" + path + "/" + filename var nextFileName = images[next] var prevFileName = images[previous] res.sendFile(req.body.currentPath + "/" + filename); // res.render("showPhoto.ejs", {photoPath: finalPath, filename: filename, currentPath: req.body.currentPath, next: nextFileName, prev:prevFileName }); })
Forget about replacing express.static with sendFile. That's a red herring. If you want to allow only authenticated users to see an anything, then add middleware that checks to see if they are authenticated on the route that serves up whatever the anything is. It doesn't matter how the route serves up the anything, only that you authenticate the user before doing do. If the route uses static to serve up an image file, then add some authentication middleware before it. If the route uses render to generate an HTML document which includes an <img> that points to the previously mentioned static route, then add some authentication middleware before it. etc. router.post( "/view/photo", authenticationMiddleware, photoPostingEndpointFunction ); router.use( "/photos", authenticationMiddleware, express.static('path/to/protected/photos) );
Send filename from JS code to a Rails view
This is my JavaScript code: var fullPath = document.getElementById('file').value; if (fullPath) { var startIndex = (fullPath.indexOf('\\') >= 0 ? fullPath.lastIndexOf('\\') : fullPath.lastIndexOf('/')); var filename = fullPath.substring(startIndex); if (filename.indexOf('\\') === 0 || filename.indexOf('/') === 0) { filename = filename.substring(1); } } if(passAll === true){ alert(filename); } As the title sugests, how can I send that var filename to a view history (just print it there) obviously after testing that last if? My view is empty so it is free for edit. Another question is if the names are going to be saved or discarded after dropping the server? And here is my controller history_controller.rb: class HistoryController < ApplicationController def historico end end
Try running rails routes in the command line of your project directory. Assuming that HistoryController is in routes.rb, it will show the routes available to send or request data from your controller. It should some something like POST /history history#create. That means you can send your filename via an HTTP POST request {RAILS ROOT URL}/history?filename={THE FILENAME} (if you are developing locally, it will probably be something like http://localhost:3000/history?filename={THE FILENAME}. Then you can access the filename in the create action of your HistoryController with the syntax params[:filename].
httpChannel.redirectTo() infinite load
I'm working on a firefox extension for the first time, and thanks to the documentation, it's going on pretty fast. I've a problem however : I wan't to redirect the users if they go on some domains. const {Cc, Ci, Cr, Cu} = require("chrome"); const buttons = require('sdk/ui/button/action'); const tabs = require("sdk/tabs"); var httpRequestObserver = { observe: function(subject, topic, data) { if (topic == "http-on-modify-request") { var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel); var eTLDService = Cc["#mozilla.org/network/effective-tld-service;1"].getService(Ci.nsIEffectiveTLDService); var suffix = eTLDService.getPublicSuffixFromHost(httpChannel.originalURI.host); var regexp = new RegExp('google\.'+suffix,'i'); if (regexp.test(httpChannel.originalURI.host)) { Cu.import("resource://gre/modules/Services.jsm"); httpChannel.redirectTo(Services.io.newURI("http://test.tld", null, null)); } } get observerService() { return Cc["#mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); }, register: function() { this.observerService.addObserver(this, "http-on-modify-request", false); }, unregister: function() { this.observerService.removeObserver(this, "http-on-modify-request"); } }; httpRequestObserver.register(); I'm trying to do a little POC, but it seems to load indefinitely. Do you know what I am doing wrong?
Don't test the originalURI! It will stay the same even after a redirect /** * The original URI used to construct the channel. This is used in * the case of a redirect or URI "resolution" (e.g. resolving a * resource: URI to a file: URI) so that the original pre-redirect * URI can still be obtained. ... */ So you redirect, that creates a new channel with the same originalURI but different URI, so your test triggers again and again and again... causing the infinite redirection loop (and redirecting by this API also is not subject to the usual redirection limit). Instead test the .URI of a channel, which gives the current URI.
What is cacheTime in the sitemap node.js module?
The documentation of the sitemap node.js module does not explain what cacheTime is. Why is it needed to generate a sitemap? What is its purpose?
The cacheTime is how long the sitemap.js module will wait before regenerating the sitemap.xml file from the list of urls given to it. ie. on the first request, a sitemap.xml file is generated and placed in the cache. Subsequent requests read the sitemap from the cache, until it expires and is regenerated. I agree it could be clearer, but the source code makes it pretty clear. According to the source code at sitemap.js, line 136: // sitemap cache this.cacheEnable = false; this.cache = ''; if (cacheTime > 0) { this.cacheEnable = true; this.cacheCleanerId = setInterval(function (self) { self.clearCache(); }, cacheTime, this); } and line 187: Sitemap.prototype.toString = function () { var self = this , xml = [ '<?xml version="1.0" encoding="UTF-8"?>', '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">']; if (this.cacheEnable && this.cache) { return this.cache; } // TODO: if size > limit: create sitemapindex this.urls.forEach( function (elem, index) { // SitemapItem var smi = elem; Specifically: if (this.cacheEnable && this.cache) { return this.cache; } And the clear cache operation has a setInterval on it equal to the cacheTime parameter given. Note the implication that your sitemap could become out of date if your urls change and your cacheTime has not triggered a clearing of the sitemap cache.
How to get the file-path of the currently executing javascript code
I'm trying to do something like a C #include "filename.c", or PHP include(dirname(__FILE__)."filename.php") but in javascript. I know I can do this if I can get the URL a js file was loaded from (e.g. the URL given in the src attribute of the tag). Is there any way for the javascript to know that? Alternatively, is there any good way to load javascript dynamically from the same domain (without knowing the domain specifically)? For example, lets say we have two identical servers (QA and production) but they clearly have different URL domains. Is there a way to do something like include("myLib.js"); where myLib.js will load from the domain of the file loading it? Sorry if thats worded a little confusingly.
Within the script: var scripts = document.getElementsByTagName("script"), src = scripts[scripts.length-1].src; This works because the browser loads and executes scripts in order, so while your script is executing, the document it was included in is sure to have your script element as the last one on the page. This code of course must be 'global' to the script, so save src somewhere where you can use it later. Avoid leaking global variables by wrapping it in: (function() { ... })();
All browsers except Internet Explorer (any version) have document.currentScript, which always works always (no matter how the file was included (async, bookmarklet etc)). If you want to know the full URL of the JS file you're in right now: var script = document.currentScript; var fullUrl = script.src; Tadaa.
I just made this little trick : window.getRunningScript = () => { return () => { return new Error().stack.match(/([^ \n])*([a-z]*:\/\/\/?)*?[a-z0-9\/\\]*\.js/ig)[0] } } console.log('%c Currently running script:', 'color: blue', getRunningScript()()) ✅ Works on: Chrome, Firefox, Edge, Opera Enjoy !
The accepted answer here does not work if you have inline scripts in your document. To avoid this you can use the following to only target <script> tags with a [src] attribute. /** * Current Script Path * * Get the dir path to the currently executing script file * which is always the last one in the scripts array with * an [src] attr */ var currentScriptPath = function () { var scripts = document.querySelectorAll( 'script[src]' ); var currentScript = scripts[ scripts.length - 1 ].src; var currentScriptChunks = currentScript.split( '/' ); var currentScriptFile = currentScriptChunks[ currentScriptChunks.length - 1 ]; return currentScript.replace( currentScriptFile, '' ); } This effectively captures the last external .js file, solving some issues I encountered with inline JS templates.
Refining upon the answers found here I came up with the following: getCurrentScript.js var getCurrentScript = function() { if (document.currentScript) { return document.currentScript.src; } else { var scripts = document.getElementsByTagName('script'); return scripts[scripts.length - 1].src; } } // module.exports = getCurrentScript; console.log({log: getCurrentScript()}) getCurrentScriptPath.js var getCurrentScript = require('./getCurrentScript'); var getCurrentScriptPath = function () { var script = getCurrentScript(); var path = script.substring(0, script.lastIndexOf('/')); return path; }; module.exports = getCurrentScriptPath; BTW: I'm using CommonJS module format and bundling with webpack.
I've more recently found a much cleaner approach to this, which can be executed at any time, rather than being forced to do it synchronously when the script loads. Use stackinfo to get a stacktrace at a current location, and grab the info.file name off the top of the stack. info = stackinfo() console.log('This is the url of the script '+info[0].file)
I've coded a simple function which allows to get the absolute location of the current javascript file, by using a try/catch method. // Get script file location // doesn't work for older browsers var getScriptLocation = function() { var fileName = "fileName"; var stack = "stack"; var stackTrace = "stacktrace"; var loc = null; var matcher = function(stack, matchedLoc) { return loc = matchedLoc; }; try { // Invalid code 0(); } catch (ex) { if(fileName in ex) { // Firefox loc = ex[fileName]; } else if(stackTrace in ex) { // Opera ex[stackTrace].replace(/called from line \d+, column \d+ in (.*):/gm, matcher); } else if(stack in ex) { // WebKit, Blink and IE10 ex[stack].replace(/at.*?\(?(\S+):\d+:\d+\)?$/g, matcher); } return loc; } }; You can see it here.
Refining upon the answers found here: little trick getCurrentScript and getCurrentScriptPath I came up with the following: //Thanks to https://stackoverflow.com/a/27369985/5175935 var getCurrentScript = function() { if (document.currentScript && (document.currentScript.src !== '')) return document.currentScript.src; var scripts = document.getElementsByTagName('script'), str = scripts[scripts.length - 1].src; if (str !== '') return str ; //Thanks to https://stackoverflow.com/a/42594856/5175935 return new Error().stack.match(/(https?:[^:]*)/)[0]; }; //Thanks to https://stackoverflow.com/a/27369985/5175935 var getCurrentScriptPath = function() { var script = getCurrentScript(), path = script.substring(0, script.lastIndexOf('/')); return path; }; console.log({path: getCurrentScriptPath()})
Regardless of whether its a script, a html file (for a frame, for example), css file, image, whatever, if you dont specify a server/domain the path of the html doc will be the default, so you could do, for example, <script type=text/javascript src='/dir/jsfile.js'></script> or <script type=text/javascript src='../../scripts/jsfile.js'></script> If you don't provide the server/domain, the path will be relative to either the path of the page or script of the main document's path
I may be misunderstanding your question but it seems you should just be able to use a relative path as long as the production and development servers use the same path structure. <script language="javascript" src="js/myLib.js" />
I've thrown together some spaghetti code that will get the current .js file ran (ex. if you run a script with "node ." you can use this to get the directory of the script that's running) this gets it as "file://path/to/directoryWhere/fileExists" var thisFilesDirectoryPath = stackinfo()[0].traceline.substring("readFile (".length, stackinfo()[0].traceline.length - ")".length-"readFile (".length); this requires an npm package (im sure its on other platforms as well): npm i stackinfo import stackinfo from 'stackinfo'; or var {stackinfo} = require("stackinfo");
function getCurrnetScriptName() { const url = new URL(document.currentScript.src); const {length:len, [len-1]:last} = url.pathname.split('/'); return last.slice(0,-3); }