Which (batch) language to learn for merging sourcefiles? - javascript

I'm creating a Greasemonkey/UserScript script. Because of the Greasemonkey sandbox I have to keep everything in one file but at 5k+ lines, maintenance is beginning to become rather difficult.
So I want to split up the script in multiple files and then combine them again for testing and/or releasing. I'd also have to be able to add some logic: Releases are per language for example (I don't want to ship German translations for the English release etc:).
As per the Pragmatic Programmer Tip 8 Invest Regularly in Your Knowledge Portfolio I'd like to learn some language to do this for me. What would be a good choice to merge the files rapidly and easily: makefile? Perl? RequireJS? Visual Studio macro's (I use VS.NET to write the UserScript)? Something else?

This is a bit of a late response, but if all you need to do is merge specific files, you should be able to do it from the command line with a batch script.
Something like:
#ECHO off
COPY file1.txt+file2.txt combined.txt

I rolled my own solution with Autohotkey. I had better used something else but well here is the ahk source:
inputFile := "sourceFileName"
savePath := "C:\Temp\"
saveAs := "targetFileName"
workingDirectory = %A_WorkingDir%
SetWorkingDir, %A_ScriptDir%
ParseFile(fileName, indentCount)
{
if not FileExist(fileName)
MsgBox Couldn't find: %fileName%
replacedFile =
Loop, Read, %fileName%
{
replacedFile .= ParseLine(A_LoopReadLine, indentCount) . "`r"
}
StringTrimRight, replacedFile, replacedFile, 1
return %replacedFile%
}
ParseLine(line, indentCount)
{
found =
FoundInclude := RegExMatch(line, "(^\s*)?//\<!--##INCLUDE "".*"" INDENT=\d //--\>", found)
if FoundInclude
{
; //<!--##INCLUDE "importit.txt" INDENT=X //-->
toIncludeFileName := RegExReplace(found, "^\s*")
StringMid, toIncludeFileName, toIncludeFileName, 18
closingQuotePosition := InStr(toIncludeFileName, """")
StringMid, newIndent, toIncludeFileName, closingQuotePosition + 9
StringMid, newIndent, newIndent, 1, 1
StringMid, toIncludeFileName, toIncludeFileName, 1, closingQuotePosition - 1
If toIncludeFileName
{
toIncludeContent := ParseFile(toIncludeFileName, newIndent)
StringReplace, line, line, %found%, %toIncludeContent%
}
else
{
StringReplace, line, line, %found%
}
}
else if indentCount
{
Loop %indentCount%
{
;line := " " . line
line := A_TAB . line
}
}
return %line%
}
; Keep backups of merges?
IfExist, %savePath%%saveAs%
{
backupCount := 0
backupFileName = %savePath%%saveAs%
while FileExist(backupFileName)
{
backupFileName = backup\%saveAs%%backupCount%
backupCount++
}
FileMove, %savePath%%saveAs%, %backupFileName%
FileCopy, %inputFile%, %backupFileName%_source
}
formattedOutput := ParseFile(inputFile, 0)
;fullFileName = %savePath%%SaveAs%
;MsgBox, %A_FileEncoding%
;file := FileOpen, fullFileName, "w"
FileEncoding, UTF-8-RAW
FileAppend, %formattedOutput%, %savePath%%SaveAs%
SetWorkingDir, workingDirectory
return
The sourceFileName looks like this:
function ready() {
var version = "//<!--##INCLUDE "version.txt" INDENT=0 //-->";
// User config
var user_data = {};
//<!--##INCLUDE "config\settings.js" INDENT=1 //-->
... more code ...
}
So the syntax for including a file is: //<!--##INCLUDE "fileToInclude" INDENT=X //--> with X being the indent level.

Related

How to interpret the number returned from statSync

I want to get the file states of a specific .js file. so i used the below posted code. but the first log statement returned
33204
while the second one returned:
144
Output:
Stats {
dev: 2049,
mode: 33204,
nlink: 2,
uid: 1000,
}
but I want to interpret these number to know if the file is accessible for (read, write) or not
code:
const fs = require('fs');
var mode = fs.statSync('../../var/opt/personal/guest/op/op_12201/data/persGuesOapDataFolder00/test0.js').mode;
var writePermissions = mode & 0x92; // 010010010
console.log(mode);
console.log(writePermissions);
this mode indicates some info regarding the file like:
Permissions (for user, group and other)
type of file
To filter a bit this data, there are some masks defined at OS to extract the info we need. These masks are:
* For filter permissions (user, group and other)
The following mask values are defined for the file mode component of the st_mode field:
S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)
S_IRWXU 00700 owner has read, write, and execute permission
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 group has read, write, and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others (not in group) have read, write, and
execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
* For detect type of file
The following mask values are defined for the file type:
S_IFMT 0170000 bit mask for the file type bit field
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 FIFO
Scenario 1: The file has read access (for everyone)
const fs = require('fs');
const mode = fs.statSync('./yourfile.txt').mode;
if (mode & (fs.constants.S_IRUSR | fs.constants.S_IRGRP | fs.constants.S_IROTH)) {
console.log('file has read permissions');
}
Scenario 2: Is a symbolic link
if (mode & fs.constants.S_IFLNK) {
console.log("It's a symbolic link");
} else {
console.log("It's not a symbolic link");
}
Hope this helps you to understand how OS works (unix systems). More info: http://man7.org/linux/man-pages/man7/inode.7.html
check if a stat object is readable or writable
useful to check if a pipe is readable or writable
const fs = require('fs');
const os = require('os');
function checkAccess(s, check="r") {
// check if s=fs.statSync(path) is readable or writable
const { uid: u, gid: g } = os.userInfo();
const m = s.mode;
if (check == "r") {
return (
((s.uid == u) && (m & fs.constants.S_IRUSR)) ||
((s.gid == g) && (m & fs.constants.S_IRGRP)) ||
(m & fs.constants.S_IROTH)
) != 0;
}
if (check == "w") {
return (
((s.uid == u) && (m & fs.constants.S_IWUSR)) ||
((s.gid == g) && (m & fs.constants.S_IWGRP)) ||
(m & fs.constants.S_IWOTH)
) != 0;
}
throw Exception("check must be r or w");
}
var s = fs.fstatSync(0); // fd 0 == stdin
console.log("fd 0 is readable?", checkAccess(s, "r"));
var s = fs.fstatSync(1); // fd 1 == stdout
console.log("fd 1 is writable?", checkAccess(s, "w"));
fs.writeSync(1, "hello\n");
see also: same thing in python
probably breaks on windows

Javascript isPunct function

I'm currently coding a Command Line program for function isPunct in Javascript and struggling to both start & completely finish it. Here's what I have so far:
function isPunct(str) {
var str = ".,:!?;";
if (/\pPunct/.test(str)) {
alert("Character is punctuation")
}
else {
alert("Character is not punctuation")
}
}
It's going through the console just fine, but isn't actually picking out the punctuation. Please help if you can! And thanks in advance.
function isPunct() {
//var str = "a.,:!?;"; -> false because of a
var str = ".,:!?;";
if (/^(\.|\,|\!|\?|\:|\;|\"|\'|\-|\(|\))*$/g.test(str)) {
alert("Character is punctuation")
}
else {
alert("Character is not punctuation")
}
}
isPunct();
It looks as if you were attempting to use Unicode property escapes (\p{UnicodePropertyName=UnicodePropertyValue}), which are not currently a part of the JavaScript standard. There is a proposal to add this functionality, but it has thus far only reached stage 2 of the TC39 process.
However, Mathias Bynens has created a wonderful tool called regexpu that will transpile these property escapes to their standard equivalents. I doubt all of the codepoints in the result are necessary to you, so feel free to shorten the list of characters as you see fit.
(Permalink to the ES5 equivalent of /^\p{Punctuation}*$/u.)
var onlyPunctuation = /^(?:[!-#%-\*,-/:;\?#\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E44\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD807[\uDC41-\uDC45\uDC70\uDC71]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F])*$/
function isPunct (string) {
return onlyPunctuation.test(string)
}
console.log(isPunct('.,:!?;')) //=> true
console.log(isPunct('letters')) //=> false

How to compile a simple Command-line OCaml script into Javascript

I have a simple command line OCaml application that performs a computation on Sys.argv.(1) and outputs the result to stdout. I can compile it to Javascript with js_of_ocaml, but it gives me a lot of errors about caml_ml_output_char being undefined. I fixed those errors by stubbing out the printfs, so it runs, but it freezes firefox while running.
How can I cleanly compile simple OCaml command-line script into a Javascript based webpage; without maintaining a forked version or freezing the browser?
You will probably want to use webworkers, as running software not designed around Javascript's co-operative multi-tasking in the UI thread can cause the browser to lock up.
You can add the following header to the top of your OCaml file to overload the normal OCaml Sys and print implementations
(* JsHeader.ml *)
let output_buffer_ = Buffer.create 1000
let flush x=let module J = Js.Unsafe in let () = J.call
(J.variable "postMessage") (J.variable "self")
[|J.inject (Js.string (Buffer.contents output_buffer_))|]
in Buffer.clear output_buffer_
let print_string = Buffer.add_string output_buffer_
let print_char = Buffer.add_char output_buffer_
let print_newline () = print_char '\n'
let print_endline s = print_string (s^"\n"); flush ()
let caml_ml_output_char = print_char
let printf fmt = Printf.bprintf output_buffer_ fmt
module Printf = struct
include Printf
let printf fmt = Printf.bprintf output_buffer_ fmt
end
The most natural way to pass in commandline arguments is through the URL sent to the web worker. We can override the Ocaml Sys module to instead read ?argv as a sequence of null terminated strings.
module Sys = struct
let char_split delim s = (*Str.split is overkill*)
let hd = ref "" in let l = ref [] in
String.iter (fun c ->
if c = delim
then (l := (!hd)::(!l); hd := "")
else hd := (!hd) ^ (String.make 1 c)
) s;
List.rev ((!hd)::(!l))
let getenv x = List.assoc x Url.Current.arguments
let argv = Array.of_list (char_split '\x00' (getenv "?argv"))
let executable_name = argv.(0)
end
Now that we have entered the header we can enter a simple OCaml Command Line program:
(* cli.ml *)
let _ = print_string (Array.fold_left (^) "" (Array.make 40 (String.lowercase (Sys.argv.(1)^"\n"))) )
This command line program relies on the OS to flush the output, but we will have to manually flush the output.
You may also want to send a null character so the Javascript knows that the command has finished.
This can be achieved by appending the following footer.
(* JsFooter.ml *)
let _ = flush stdout; print_endline "\x00"
We can join the files and compile them as follows:
cat JsHeader.ml cli.ml JsFooter.ml > merged.ml
ocamlbuild -use-menhir -menhir "menhir" \
-pp "camlp4o -I /opt/local/lib/ocaml/site-lib js_of_ocaml/pa_js.cmo" \
-cflags -I,+js_of_ocaml,-I,+site-lib/js_of_ocaml -libs js_of_ocaml \
-lflags -I,+js_of_ocaml,-I,+site-lib/js_of_ocaml merged.byte
js_of_ocaml merged.byte
Now that we have created the file merged.js we can wrap the javascript in a simple web page such as the following:
<html>
<head>
<meta http-equiv="Content-Type" content="text/xhtml+xml; charset=UTF-8" />
<title>ml2js sample_cli</title>
<script type="text/javascript">
<!--
var worker;
function go () {
var output=document.getElementById ("output");
var argv = encodeURIComponent("/bin/sample_cli\0"+document.getElementById ("input").value);
if (worker) {
worker.terminate();
}
worker = new Worker ("sample_cli.js?argv="+argv);
document.getElementById ("output").value="";
worker.onmessage = function (m) {
if (typeof m.data == 'string') {
if (m.data == "\0\n") {
output.scrollTop = output.scrollHeight
} else {
output.value+=m.data;
}
}
}
}
//-->
</script>
</head>
<body onload=go()>
<textarea id="input" rows="2" cols="60" onkeyup="go()" onchange="go()" style="width:90%">SAMPLE_INPUT</textarea>
<button onclick="go()">go</button><br>
<textarea id="output" rows="0" cols="60" style="width:100%;height:90%" readonly onload=go()>
Your browser does not seem to support Webworkers.
Try Firefox, Chrome or IE10+.
</textarea>
</body>
</html>
See http://www.dansted.org/app/bctl-plain.html for an example of this approach in action, and https://github.com/gmatht/TimeLogicUnify/blob/master/ATL/js/webworker/ml2js.sh for a script that appends the appropriate headers, footers etc.
What js_of_ocaml's version are you using ? You should not get errors with caml_ml_output_char. When running on node, you should have sys.argv set correctly. In the browser, Sys.argv is set to [|"a.out"|].
Please open a GitHub issue on https://github.com/ocsigen/js_of_ocaml/issues/new if you still have an issue with this.

Unexpected Token Illegal with onclick Java Script in Salesforce.com

I have been working on this most of the morning but to no end. I am trying to execute a button that uses OnClick Java in Salesforce.com and it keeps throwing errors. I think the issue may be with special characters in the data as it works when I simply use just text. But any time numbers or any special characters are present I get the error "unexpected token ILLEGAL". Can anyone help me to see what I am doing wrong and how I can get away from failing when special characters are involved?
{!REQUIRESCRIPT("/soap/ajax/28.0/connection.js")}
var opptyObj = new sforce.SObject("Opportunity");
var caseObj = new sforce.SObject("Case");
var today = new Date();
var sOpptyId = "{!Case.Opportunity__c}";
if( sOpptyId != "")
{
alert("This case is already tied to an opportunity!");
}
else
{
opptyObj.AccountId = "{!Case.AccountId}";
opptyObj.CloseDate = sforce.internal.dateTimeToString(today);
opptyObj.Description="{!Case.Description}";
opptyObj.Case__c = "{!Case.Id}";
opptyObj.Name = "{!Case.Subject}";
opptyObj.StageName = "Estimate in Progress";
opptyObj.Created_from_Case__c = "Y";
opptyObj.Type = "New Business";
opptyObj.Amount = ".01";
var opptyresult = sforce.connection.create([opptyObj]);
if (opptyresult[0].success=='false')
{
alert("Opportunity creation failed: " + opptyresult[0].errors.message);
}
else
{
caseObj.Id = '{!Case.Id}';
caseObj.Opportunity__c = opptyresult[0].id;
caseObj.Status = "Estimate in Progress";
var caseResult = sforce.connection.update([caseObj]);
if(caseResult[0].success == 'false')
{
alert("Case update failed: " + caseResult[0].errors.message);
}
else
{
alert("An opportunity has been created and linked to this case.");
location.reload(true);
}
}
}
Assuming this is some kind of template, whatever is rendering this needs to properly escape some values in the strings it's inserting.
Given this:
opptyObj.Description="{!Case.Description}";
Let's say I enter a description consisting of this:
"That is awesome," said John.
When that is rendered in your template the result is this:
opptyObj.Description=""That is awesome," said John.";
As you might be able to see, the result is a syntax error.
You need to escape quote characters in an text inserted this way. And without knowing what is technology rendering this template I can't give you any specifics, but you want to replace " with \" and ' with \'. The \ escapes characters, forcing them to be treated as literal characters in the string instead of other special meaning.
This must be done as it's being inserted into the script. Something in the spirit of this:
opptyObj.Description="{!Case.Description.replace(/'/, "\\'").replace(/"/, '\\"')}
Exactly how to do that depends on what language or template engine is being used here. But th eresult should look like this:
opptyObj.Description="\"That is awesome,\" said John.";
Ruby on Rails implements an escape_javascript method, which sanitizes data for injection into Javascript. It does the following replacements. It seems like a good baseline.
'\\' => '\\\\'
'</' => '<\/'
"\r\n" => '\n'
"\n" => '\n'
"\r" => '\n'
'"' => '\\"'
"'" => "\\'"
UPDATE:
According to this: http://www.salesforce.com/us/developer/docs/pages/Content/pages_security_tips_scontrols.htm
It looks like you want the JSENCODE function. Something like this, perhaps?
opptyObj.Description="{!JSENCODE(Case.Description)}";

How can I get file extensions with JavaScript?

See code:
var file1 = "50.xsl";
var file2 = "30.doc";
getFileExtension(file1); //returns xsl
getFileExtension(file2); //returns doc
function getFileExtension(filename) {
/*TODO*/
}
Newer Edit: Lots of things have changed since this question was initially posted - there's a lot of really good information in wallacer's revised answer as well as VisioN's excellent breakdown
Edit: Just because this is the accepted answer; wallacer's answer is indeed much better:
return filename.split('.').pop();
My old answer:
return /[^.]+$/.exec(filename);
Should do it.
Edit: In response to PhiLho's comment, use something like:
return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename) : undefined;
return filename.split('.').pop();
Edit:
This is another non-regex solution that I think is more efficient:
return filename.substring(filename.lastIndexOf('.')+1, filename.length) || filename;
There are some corner cases that are better handled by VisioN's answer below, particularly files with no extension (.htaccess etc included).
It's very performant, and handles corner cases in an arguably better way by returning "" instead of the full string when there's no dot or no string before the dot. It's a very well crafted solution, albeit tough to read. Stick it in your helpers lib and just use it.
Old Edit:
A safer implementation if you're going to run into files with no extension, or hidden files with no extension (see VisioN's comment to Tom's answer above) would be something along these lines
var a = filename.split(".");
if( a.length === 1 || ( a[0] === "" && a.length === 2 ) ) {
return "";
}
return a.pop(); // feel free to tack .toLowerCase() here if you want
If a.length is one, it's a visible file with no extension ie. file
If a[0] === "" and a.length === 2 it's a hidden file with no extension ie. .htaccess
This should clear up issues with the slightly more complex cases. In terms of performance, I think this solution is a little slower than regex in most browsers. However, for most common purposes this code should be perfectly usable.
The following solution is fast and short enough to use in bulk operations and save extra bytes:
return fname.slice((fname.lastIndexOf(".") - 1 >>> 0) + 2);
Here is another one-line non-regexp universal solution:
return fname.slice((Math.max(0, fname.lastIndexOf(".")) || Infinity) + 1);
Both work correctly with names having no extension (e.g. myfile) or starting with . dot (e.g. .htaccess):
"" --> ""
"name" --> ""
"name.txt" --> "txt"
".htpasswd" --> ""
"name.with.many.dots.myext" --> "myext"
If you care about the speed you may run the benchmark and check that the provided solutions are the fastest, while the short one is tremendously fast:
How the short one works:
String.lastIndexOf method returns the last position of the substring (i.e. ".") in the given string (i.e. fname). If the substring is not found method returns -1.
The "unacceptable" positions of dot in the filename are -1 and 0, which respectively refer to names with no extension (e.g. "name") and to names that start with dot (e.g. ".htaccess").
Zero-fill right shift operator (>>>) if used with zero affects negative numbers transforming -1 to 4294967295 and -2 to 4294967294, which is useful for remaining the filename unchanged in the edge cases (sort of a trick here).
String.prototype.slice extracts the part of the filename from the position that was calculated as described. If the position number is more than the length of the string method returns "".
If you want more clear solution which will work in the same way (plus with extra support of full path), check the following extended version. This solution will be slower than previous one-liners but is much easier to understand.
function getExtension(path) {
var basename = path.split(/[\\/]/).pop(), // extract file name from full path ...
// (supports `\\` and `/` separators)
pos = basename.lastIndexOf("."); // get last position of `.`
if (basename === "" || pos < 1) // if file name is empty or ...
return ""; // `.` not found (-1) or comes first (0)
return basename.slice(pos + 1); // extract extension ignoring `.`
}
console.log( getExtension("/path/to/file.ext") );
// >> "ext"
All three variants should work in any web browser on the client side and can be used in the server side NodeJS code as well.
function getFileExtension(filename)
{
var ext = /^.+\.([^.]+)$/.exec(filename);
return ext == null ? "" : ext[1];
}
Tested with
"a.b" (=> "b")
"a" (=> "")
".hidden" (=> "")
"" (=> "")
null (=> "")
Also
"a.b.c.d" (=> "d")
".a.b" (=> "b")
"a..b" (=> "b")
There is a standard library function for this in the path module:
import path from 'path';
console.log(path.extname('abc.txt'));
Output:
.txt
So, if you only want the format:
path.extname('abc.txt').slice(1) // 'txt'
If there is no extension, then the function will return an empty string:
path.extname('abc') // ''
If you are using Node, then path is built-in. If you are targetting the browser, then Webpack will bundle a path implementation for you. If you are targetting the browser without Webpack, then you can include path-browserify manually.
There is no reason to do string splitting or regex.
function getExt(filename)
{
var ext = filename.split('.').pop();
if(ext == filename) return "";
return ext;
}
var extension = fileName.substring(fileName.lastIndexOf('.')+1);
If you are dealing with web urls, you can use:
function getExt(filepath){
return filepath.split("?")[0].split("#")[0].split('.').pop();
}
getExt("../js/logic.v2.min.js") // js
getExt("http://example.net/site/page.php?id=16548") // php
getExt("http://example.net/site/page.html#welcome.to.me") // html
getExt("c:\\logs\\yesterday.log"); // log
Demo: https://jsfiddle.net/squadjot/q5ard4fj/
var parts = filename.split('.');
return parts[parts.length-1];
function file_get_ext(filename)
{
return typeof filename != "undefined" ? filename.substring(filename.lastIndexOf(".")+1, filename.length).toLowerCase() : false;
}
Code
/**
* Extract file extension from URL.
* #param {String} url
* #returns {String} File extension or empty string if no extension is present.
*/
var getFileExtension = function (url) {
"use strict";
if (url === null) {
return "";
}
var index = url.lastIndexOf("/");
if (index !== -1) {
url = url.substring(index + 1); // Keep path without its segments
}
index = url.indexOf("?");
if (index !== -1) {
url = url.substring(0, index); // Remove query
}
index = url.indexOf("#");
if (index !== -1) {
url = url.substring(0, index); // Remove fragment
}
index = url.lastIndexOf(".");
return index !== -1
? url.substring(index + 1) // Only keep file extension
: ""; // No extension found
};
Test
Notice that in the absence of a query, the fragment might still be present.
"https://www.example.com:8080/segment1/segment2/page.html?foo=bar#fragment" --> "html"
"https://www.example.com:8080/segment1/segment2/page.html#fragment" --> "html"
"https://www.example.com:8080/segment1/segment2/.htaccess?foo=bar#fragment" --> "htaccess"
"https://www.example.com:8080/segment1/segment2/page?foo=bar#fragment" --> ""
"https://www.example.com:8080/segment1/segment2/?foo=bar#fragment" --> ""
"" --> ""
null --> ""
"a.b.c.d" --> "d"
".a.b" --> "b"
".a.b." --> ""
"a...b" --> "b"
"..." --> ""
JSLint
0 Warnings.
Fast and works correctly with paths
(filename.match(/[^\\\/]\.([^.\\\/]+)$/) || [null]).pop()
Some edge cases
/path/.htaccess => null
/dir.with.dot/file => null
Solutions using split are slow and solutions with lastIndexOf don't handle edge cases.
// 获取文件后缀名
function getFileExtension(file) {
var regexp = /\.([0-9a-z]+)(?:[\?#]|$)/i;
var extension = file.match(regexp);
return extension && extension[1];
}
console.log(getFileExtension("https://www.example.com:8080/path/name/foo"));
console.log(getFileExtension("https://www.example.com:8080/path/name/foo.BAR"));
console.log(getFileExtension("https://www.example.com:8080/path/name/.quz/foo.bar?key=value#fragment"));
console.log(getFileExtension("https://www.example.com:8080/path/name/.quz.bar?key=value#fragment"));
i just wanted to share this.
fileName.slice(fileName.lastIndexOf('.'))
although this has a downfall that files with no extension will return last string.
but if you do so this will fix every thing :
function getExtention(fileName){
var i = fileName.lastIndexOf('.');
if(i === -1 ) return false;
return fileName.slice(i)
}
"one-liner" to get filename and extension using reduce and array destructuring :
var str = "filename.with_dot.png";
var [filename, extension] = str.split('.').reduce((acc, val, i, arr) => (i == arr.length - 1) ? [acc[0].substring(1), val] : [[acc[0], val].join('.')], [])
console.log({filename, extension});
with better indentation :
var str = "filename.with_dot.png";
var [filename, extension] = str.split('.')
.reduce((acc, val, i, arr) => (i == arr.length - 1)
? [acc[0].substring(1), val]
: [[acc[0], val].join('.')], [])
console.log({filename, extension});
// {
// "filename": "filename.with_dot",
// "extension": "png"
// }
There's also a simple approach using ES6 destructuring:
const path = 'hello.world.txt'
const [extension, ...nameParts] = path.split('.').reverse();
console.log('extension:', extension);
function extension(fname) {
var pos = fname.lastIndexOf(".");
var strlen = fname.length;
if (pos != -1 && strlen != pos + 1) {
var ext = fname.split(".");
var len = ext.length;
var extension = ext[len - 1].toLowerCase();
} else {
extension = "No extension found";
}
return extension;
}
//usage
extension('file.jpeg')
always returns the extension lower cas so you can check it on field change
works for:
file.JpEg
file (no extension)
file. (noextension)
This simple solution
function extension(filename) {
var r = /.+\.(.+)$/.exec(filename);
return r ? r[1] : null;
}
Tests
/* tests */
test('cat.gif', 'gif');
test('main.c', 'c');
test('file.with.multiple.dots.zip', 'zip');
test('.htaccess', null);
test('noextension.', null);
test('noextension', null);
test('', null);
// test utility function
function test(input, expect) {
var result = extension(input);
if (result === expect)
console.log(result, input);
else
console.error(result, input);
}
function extension(filename) {
var r = /.+\.(.+)$/.exec(filename);
return r ? r[1] : null;
}
I'm sure someone can, and will, minify and/or optimize my code in the future. But, as of right now, I am 200% confident that my code works in every unique situation (e.g. with just the file name only, with relative, root-relative, and absolute URL's, with fragment # tags, with query ? strings, and whatever else you may decide to throw at it), flawlessly, and with pin-point precision.
For proof, visit: https://projects.jamesandersonjr.com/web/js_projects/get_file_extension_test.php
Here's the JSFiddle: https://jsfiddle.net/JamesAndersonJr/ffcdd5z3/
Not to be overconfident, or blowing my own trumpet, but I haven't seen any block of code for this task (finding the 'correct' file extension, amidst a battery of different function input arguments) that works as well as this does.
Note: By design, if a file extension doesn't exist for the given input string, it simply returns a blank string "", not an error, nor an error message.
It takes two arguments:
String: fileNameOrURL (self-explanatory)
Boolean: showUnixDotFiles (Whether or Not to show files that begin with a dot ".")
Note (2): If you like my code, be sure to add it to your js library's, and/or repo's, because I worked hard on perfecting it, and it would be a shame to go to waste. So, without further ado, here it is:
function getFileExtension(fileNameOrURL, showUnixDotFiles)
{
/* First, let's declare some preliminary variables we'll need later on. */
var fileName;
var fileExt;
/* Now we'll create a hidden anchor ('a') element (Note: No need to append this element to the document). */
var hiddenLink = document.createElement('a');
/* Just for fun, we'll add a CSS attribute of [ style.display = "none" ]. Remember: You can never be too sure! */
hiddenLink.style.display = "none";
/* Set the 'href' attribute of the hidden link we just created, to the 'fileNameOrURL' argument received by this function. */
hiddenLink.setAttribute('href', fileNameOrURL);
/* Now, let's take advantage of the browser's built-in parser, to remove elements from the original 'fileNameOrURL' argument received by this function, without actually modifying our newly created hidden 'anchor' element.*/
fileNameOrURL = fileNameOrURL.replace(hiddenLink.protocol, ""); /* First, let's strip out the protocol, if there is one. */
fileNameOrURL = fileNameOrURL.replace(hiddenLink.hostname, ""); /* Now, we'll strip out the host-name (i.e. domain-name) if there is one. */
fileNameOrURL = fileNameOrURL.replace(":" + hiddenLink.port, ""); /* Now finally, we'll strip out the port number, if there is one (Kinda overkill though ;-)). */
/* Now, we're ready to finish processing the 'fileNameOrURL' variable by removing unnecessary parts, to isolate the file name. */
/* Operations for working with [relative, root-relative, and absolute] URL's ONLY [BEGIN] */
/* Break the possible URL at the [ '?' ] and take first part, to shave of the entire query string ( everything after the '?'), if it exist. */
fileNameOrURL = fileNameOrURL.split('?')[0];
/* Sometimes URL's don't have query's, but DO have a fragment [ # ](i.e 'reference anchor'), so we should also do the same for the fragment tag [ # ]. */
fileNameOrURL = fileNameOrURL.split('#')[0];
/* Now that we have just the URL 'ALONE', Let's remove everything to the last slash in URL, to isolate the file name. */
fileNameOrURL = fileNameOrURL.substr(1 + fileNameOrURL.lastIndexOf("/"));
/* Operations for working with [relative, root-relative, and absolute] URL's ONLY [END] */
/* Now, 'fileNameOrURL' should just be 'fileName' */
fileName = fileNameOrURL;
/* Now, we check if we should show UNIX dot-files, or not. This should be either 'true' or 'false'. */
if ( showUnixDotFiles == false )
{
/* If not ('false'), we should check if the filename starts with a period (indicating it's a UNIX dot-file). */
if ( fileName.startsWith(".") )
{
/* If so, we return a blank string to the function caller. Our job here, is done! */
return "";
};
};
/* Now, let's get everything after the period in the filename (i.e. the correct 'file extension'). */
fileExt = fileName.substr(1 + fileName.lastIndexOf("."));
/* Now that we've discovered the correct file extension, let's return it to the function caller. */
return fileExt;
};
Enjoy! You're Quite Welcome!:
Try this:
function getFileExtension(filename) {
var fileinput = document.getElementById(filename);
if (!fileinput)
return "";
var filename = fileinput.value;
if (filename.length == 0)
return "";
var dot = filename.lastIndexOf(".");
if (dot == -1)
return "";
var extension = filename.substr(dot, filename.length);
return extension;
}
If you are looking for a specific extension and know its length, you can use substr:
var file1 = "50.xsl";
if (file1.substr(-4) == '.xsl') {
// do something
}
JavaScript reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr
I just realized that it's not enough to put a comment on p4bl0's answer, though Tom's answer clearly solves the problem:
return filename.replace(/^.*?\.([a-zA-Z0-9]+)$/, "$1");
For most applications, a simple script such as
return /[^.]+$/.exec(filename);
would work just fine (as provided by Tom). However this is not fool proof. It does not work if the following file name is provided:
image.jpg?foo=bar
It may be a bit overkill but I would suggest using a url parser such as this one to avoid failure due to unpredictable filenames.
Using that particular function, you could get the file name like this:
var trueFileName = parse_url('image.jpg?foo=bar').file;
This will output "image.jpg" without the url vars. Then you are free to grab the file extension.
function func() {
var val = document.frm.filename.value;
var arr = val.split(".");
alert(arr[arr.length - 1]);
var arr1 = val.split("\\");
alert(arr1[arr1.length - 2]);
if (arr[1] == "gif" || arr[1] == "bmp" || arr[1] == "jpeg") {
alert("this is an image file ");
} else {
alert("this is not an image file");
}
}
I'm many moons late to the party but for simplicity I use something like this
var fileName = "I.Am.FileName.docx";
var nameLen = fileName.length;
var lastDotPos = fileName.lastIndexOf(".");
var fileNameSub = false;
if(lastDotPos === -1)
{
fileNameSub = false;
}
else
{
//Remove +1 if you want the "." left too
fileNameSub = fileName.substr(lastDotPos + 1, nameLen);
}
document.getElementById("showInMe").innerHTML = fileNameSub;
<div id="showInMe"></div>
A one line solution that will also account for query params and any characters in url.
string.match(/(.*)\??/i).shift().replace(/\?.*/, '').split('.').pop()
// Example
// some.url.com/with.in/&ot.s/files/file.jpg?spec=1&.ext=jpg
// jpg
return filename.replace(/\.([a-zA-Z0-9]+)$/, "$1");
edit: Strangely (or maybe it's not) the $1 in the second argument of the replace method doesn't seem to work... Sorry.
fetchFileExtention(fileName) {
return fileName.slice((fileName.lastIndexOf(".") - 1 >>> 0) + 2);
}
Wallacer's answer is nice, but one more checking is needed.
If file has no extension, it will use filename as extension which is not good.
Try this one:
return ( filename.indexOf('.') > 0 ) ? filename.split('.').pop().toLowerCase() : 'undefined';
Don't forget that some files can have no extension, so:
var parts = filename.split('.');
return (parts.length > 1) ? parts.pop() : '';

Categories

Resources