Is there any way to get passed empty folders when drag dropping folder structure using dropzone.js? - dropzone.js

I'm using dropzone.js to be able to easily upload files and folders. I do not get an notification for empty folders, is there any way to have an event fired for empty folders?
Thank you.

So this is what I came up with, not the most elegant solution, but does what I need to be able to handle emptyfolders. Would be great if dropzone.js had an option for enabling emptyfolders to be returned.
I made the following changes to the dropzone.js
At the very top I added
var passedFolders = [];
// used to store the names of fodlers that were passed, with either files or without
// the key is the path name, the value is true if empty, false if it had contents
Then under the section
var readEntries = function readEntries() {
return dirReader.readEntries(function (entries) {
change it to
var readEntries = function readEntries() {
return dirReader.readEntries(function (entries) {
if (entries.length > 0) {
var _iterator7 = dropzone_createForOfIteratorHelper(entries, true),
_step7;
try {
for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
var entry = _step7.value;
if (entry.isFile) {
//There is a a file in the folder, so we can set it's value to false
passedFolders[entry.fullPath.substring(0, entry.fullPath.lastIndexOf(entry.name)-1)] = false //-1 to remove the last slash
entry.file(function (file) {
if (_this5.options.ignoreHiddenFiles && file.name.substring(0, 1) === ".") {
return;
}
file.fullPath = "".concat(path, "/").concat(file.name);
return _this5.addFile(file);
});
} else if (entry.isDirectory) {
//This is just a folder, not a file. Store the folder path information set to true, it will remain true if the folder remains empty
passedFolders[entry.fullPath] = true;
_this5._addFilesFromDirectory(entry, "".concat(path, "/").concat(entry.name));
}
} // Recursively call readEntries() again, since browser only handle
// the first 100 entries.
// See: https://developer.mozilla.org/en-US/docs/Web/API/DirectoryReader#readEntries
} catch (err) {
_iterator7.e(err);
} finally {
_iterator7.f();
}
readEntries();
}
return null;
}, errorHandler);
};
Now passedFolders will contain an associate array, where the key is the pathname and the value is true if it is empty, false if it contained files.
Then under the dropzone init, when the file uploads are complete you can choose what to do with empty folders
this.on('queuecomplete', function (file, json) {
alert("Uploads complete, now we can deal with the empty folders");
for (var key in passedFolders) {
if (passedFolders[key]) {
alert(key);
}
}
});

Related

Photoshop CC2019 auto-update all linked smart objects including nested ones

I have Photoshop CC2019 PSD document containing several smart objects that contains other smart objects that contains other smart objects. Some of these have linked layers. Normally, such images are not updated automatically (which is extremely annoying, Adobe!) but you have to manually update each of them once the linked image content has changed.
There is a .jsx script file named "Update All Modified Content.jsx" which auto-updates linked layers (PNG image in my case) but only if the smart object is in the top most document - that is no nested smart objects with linked layers are updated automatically.
My question is: does anyone know how to update the content of the above mentioned .jsx file so that it would auto-update all linked images across all the smart objects in PSD document including nested ones?
For those who care or would be willing to help updating the code here it is:
// Update all modified content
var idplacedLayerUpdateAllModified = stringIDToTypeID( "placedLayerUpdateAllModified" );
executeAction( idplacedLayerUpdateAllModified, undefined, DialogModes.NO );
So, after spending half a day with it I finally solved it myself. Here is the code:
#target photoshop
// SET INITIAL ACTIVE DOCUMENT
var mainDocument = app.activeDocument;
// SAVE THE DOCUMENT NAME FOR FUTURE USE
var mainDocName = mainDocument.name;
// RUN THE MAIN UPDATE FUNCTION
mainDocument.suspendHistory("processAllSmartObjects", "autoupdateAllSmartObjects(mainDocument, 0)");
// FINALLY SAVE THE MAIN DOCUMENT
mainDocument.save();
function autoupdateAllSmartObjects(theParent, prevVal) {
// FUNCTION TO TEST IF SMARTOBJECT IS LINKED
function isLinkedSO(obj) {
var localFilePath = "";
var ref = new ActionReference();
ref.putIdentifier(charIDToTypeID('Lyr '), obj.id);
var desc = executeActionGet(ref);
var smObj = desc.getObjectValue(stringIDToTypeID('smartObject'));
var isLinked = false;
// TEST IF IT HAS LINKED FILE
try {
var localFilePath = smObj.getPath(stringIDToTypeID('link'));
isLinked = true;
} catch(e) {
//
}
return isLinked;
}
// FUNCTION TO UPDATE LINKED SMART OBJECT
function doTheUpdate(LYR, stackNr) {
// SET ACTIVE LAYER TO ACTUALY ITERATED ONE
app.activeDocument.activeLayer = LYR;
// RUN IN "SILENT" MODE
app.displayDialogs = DialogModes.NO;
var layer = app.activeDocument.activeLayer;
// IF ACTIVE LAYER IS SMARTOBJECT
if (layer.kind == "LayerKind.SMARTOBJECT") {
//alert(layer);
// OPEN THE SMARTOBJECT
app.runMenuItem(stringIDToTypeID('placedLayerEditContents'));
// DO THE ACTUAL FILE UPDATE
var idplacedLayerUpdateAllModified = stringIDToTypeID( "placedLayerUpdateAllModified" );
executeAction( idplacedLayerUpdateAllModified, undefined, DialogModes.NO);
// IF IT IS NOT THE "CORE/MAIN" DOCUMENT
if(stackNr > 0) {
// SAVE CHANGES (UPDATE) AND CLOSE IT
app.activeDocument.close(SaveOptions.SAVECHANGES);
}
// CONTINUE INSIDE THIS ACTIVE SMARTOBJECT
autoupdateAllSmartObjects(app.activeDocument, stackNr);
}
return;
}
// FUNCTION TO PARSE GROUPS
function parseGroup(LYR) {
var groupLayers = LYR.layers;
// IF GROUP IS NOT EMPTY
if(groupLayers.length > 0) {
// PARSE ALL LAYERS IN THE GROUP
for (var i = groupLayers.length - 1; i >= 0; i--) {
var lyr = groupLayers[i];
// IF NOT LOCKED = NOT EDITABL:E
if(!lyr.allLocked) {
// YET ANOTHER GROUP?
if (lyr.typename == "LayerSet") {
// IF IT IS NOT EMPTY
if (lyr.layers.length > 0) {
// RE-RUN THE SCRIPT ANEW WITH THE SELECTED GROUP AS LAYERS SOURCE
autoupdateAllSmartObjects(lyr, 0);
}
// LAYERS
} else if (lyr.typename == "ArtLayer") {
// IF THE LAYER IS SMARTOBJECT
if (lyr.kind == LayerKind.SMARTOBJECT) {
// IF THE LAYER IS SET TO "visible" (THAT IS: NOT DISABLED)
if(lyr.visible){
// TEST IF THE SMARTOBJECT IS ACTUALLY LINKED
if(!isLinkedSO(lyr)) {
// RUN THE UPDATE SUB-FUNCTION
doTheUpdate(lyr, i);
}
}
}
}
}
}
}
}
// PARSE ALL THE LAYERS
for (var i = theParent.layers.length - 1 - prevVal; i >= 0; i--) {
var theLayer = theParent.layers[i];
// ONLY ArtLayers
if (theLayer.typename == "ArtLayer") {
// IF THE LAYER IS SMARTOBJECT
if (theLayer.kind == LayerKind.SMARTOBJECT) {
// IF THE LAYER IS SET TO "visible" (THAT IS: NOT DISABLED)
if(theLayer.visible){
// TEST IF THE SMARTOBJECT IS ACTUALLY LINKED
if(!isLinkedSO(theLayer)){
// RUN THE UPDATE SUB-FUNCTION
doTheUpdate(theLayer, i);
// IF WE ARE AT THE LAST LAYER IN THE STACK AND IT IS NOT OUR MAIN DOCUMENT
if(i == 0 && app.activeDocument.name !== mainDocName) {
// SAVE CHANGES (UPDATE) AND CLOSE IT
app.activeDocument.close(SaveOptions.SAVECHANGES);
}
}
}
}
// ONLY Groups
} else if (theLayer.typename == "LayerSet") {
// RUN SUB-FUNCTION FOR GROUP PARSING
parseGroup(theLayer);
// ANYTHING ELSE
} else {
autoupdateAllSmartObjects(theLayer, m);
}
}
return;
};
OP's script works!! It was going in loops for me too but after some trial and error, I realised that my smart objects (SO) that are linked across artboards ( - e.g. if you change one SO, it changes on various artboards) - were the issue. I hid all such SO and it works.
so basically, it only works for Smart Objects + copies made via 'New smart object via copy' NOT 'duplicate layer' / copypaste smart objects. If your work contains a 'duplicate layer' SO - it will break the script. You need to hide these objects ( or not work like that all together) before running the script

getting XMP File does not have a constructor error through ExtendScript

I am using In Design CC 2019, on my Mac OS. When I am trying to get XMP data for my .indd (InDesign document) using ExtendScript.
I am currently getting the error like this:
XMPFile Does not have a constructor.
Below is my script.
// load XMP Library
function loadXMPLibrary(){
if ( ExternalObject.AdobeXMPScript){
try{ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');}
catch (e){alert('Unable to load the AdobeXMPScript library!'); return false;}
}
return true;
}
var myFile= app.activeDocument.fullName;
// check library and file
if(loadXMPLibrary() && myFile != null){
xmpFile = new XMPFile(myFile.fsName, XMPConst.FILE_INDESIGN, XMPConst.OPEN_FOR_UPDATE);
var myXmp = xmpFile.getXMP();
}
if(myXmp){
$.writeln ('sucess')
}
There's an issue with your codes logic, you need to make the following change:
Add the Logical NOT operator (i.e. !) to the condition specified for your if statement in the body of your loadXMPLibrary function.
function loadXMPLibrary(){
if (!ExternalObject.AdobeXMPScript) { // <--- Change to this
// ^
try {ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');}
catch (e){alert('Unable to load the AdobeXMPScript library!'); return false;}
}
return true;
}
You need to add this because currently your if statement checks whether the condition is truthy, i.e. it checks whether ExternalObject.AdobeXMPScript is true. This will always remain false, until the AdobeXMPScript library has been loaded, therefore you're code that actually loads the library never gets executed.
Revised script:
For clarity here is the complete revised script:
// load XMP Library
function loadXMPLibrary() {
if (!ExternalObject.AdobeXMPScript) {
try{ExternalObject.AdobeXMPScript = new ExternalObject('lib:AdobeXMPScript');}
catch (e){alert('Unable to load the AdobeXMPScript library!'); return false;}
}
return true;
}
var myFile= app.activeDocument.fullName;
// check library and file
if (loadXMPLibrary() && myFile !== null) {
xmpFile = new XMPFile(myFile.fsName, XMPConst.FILE_INDESIGN, XMPConst.OPEN_FOR_UPDATE);
var myXmp = xmpFile.getXMP();
}
if (myXmp){
$.writeln ('success')
}

Trouble w/ Meteor Sorting

I'm trying to add a simple drop down control above a list such that I can sort it by "created" or "title".
The list template is called posts_list.html. In it's helper .js file I have:
posts: function () {
var sortCriteria = Session.get("sortCriteria") || {};
return Posts.find({},{sort: {sortCriteria: 1}});
}
Then, I have abstracted the list into another template. From here I have the following click event tracker in the helper.js
"click": function () {
// console.log(document.activeElement.id);
Session.set("sortCriteria", document.activeElement.id);
// Router.go('history');
Router.render('profile');
}
Here I can confirm that the right Sort criteria is written to the session. However, I can't make the page refresh. The collection on the visible page never re-sorts.
Frustrating. Any thoughts?
Thanks!
You can't use variables as keys in an object literal. Give this a try:
posts: function() {
var sortCriteria = Session.get('sortCriteria');
var options = {};
if (sortCriteria) {
options.sort = {};
options.sort[sortCriteria] = 1;
}
return Posts.find({}, options);
}
Also see the "Variables as keys" section of common mistakes.
thanks so much for that. Note I've left commented out code below to show what I pulled out. If I required a truly dynamic option, versus the simply binary below, I would have stuck w/ the "var options" approach. What I ended up going with was:
Template.postList.helpers({
posts: function () {
//var options = {};
if (Session.get("post-list-sort")) {
/*options.sort = {};
if (Session.get("post-list-sort") == "Asc") {
options.sort['created'] = 1;
} else {
options.sort['created'] = -1;
}*/
//return hunts.find({}, options);}
console.log(Session.get("hunt-list-sort"));
if (Session.get("hunt-list-sort") == "Asc") {
return Hunts.find({}, {sort: {title: 1}});
}
else {
return Hunts.find({}, {sort: {title: -1}});
};
}
}
});

Show file properites dialog programatically or from command line in windows

Explorer, right click a single file, properties...
Shows File Properties Dialog.
I am hoping there is a direct way to do this from the command line, which explains my complete lack of research on how to do it with the win api :)
Answering my own question. I put together this script from the links provided. There is probably a more direct way -- this has a Frankenstein-like aura to it, but it works. The drawback is that there is a hack to keep the dialog open (sleep the script) that can be changed to two different methods. This script will exec any verb, as well as list the verbs.
Usage: save script as shareGUI.js, call with cscript shareGUI.js <args>
Examples:
cscript shareGUI.js C:\path\to\file.txt will list verbs
cscript shareGUI.js <verb> C:\path\to\file.txt will execute given verb
cscript shareGUI.js "Properties" C:\path\to\file.txt will show properties dialog
Include usual disclaimers in bold type.
var args = WScript.Arguments;
var usage="Usage: cscript shareGUI.js [verb] <fullpath>";
useage+="\n Function: Executes given verb on given file";
useage+="\n or lists available verbs";
useage+="\n Notes: Ampersands in property names are remove in listing";
useage+="\n Ampersands are not required when specifying verb";
useage+="\n Arguments: ";
useage+="\n verb (optional): Name of verb to be executed, case sensative";
useage+="\n if not provided, verbs are listed";
useage+="\n fullpath: FULLPATH Name of file or directory";
//
if ((args.Unnamed.length == 0) || (args.Named.Exists("?"))) {
QuitMsg(usage,0);
}
if (args.Unnamed.length > 2) {
QuitMsg(usage,0);
}
//
var tgt_;
var verb_;
if (args.Unnamed.length == 1) {
tgt_=args.Unnamed.Item(0);
verb_ = "";
}else if (args.Unnamed.length == 2) {
verb_ = args.Unnamed.Item(0);
tgt_=args.Unnamed.Item(1);
}
//
var shellApp = new ActiveXObject("Shell.Application");
QuitIfNull(shellApp,"System Error:Could not get Shell.Application ActiveXObject",2);
//
var objFSO = new ActiveXObject("Scripting.FileSystemObject");
QuitIfNull(objFSO,"System Error:Could not get Scripting.FileSystemObject ActiveXObject",2);
//
var WSHshell = new ActiveXObject("Wscript.Shell");
QuitIfNull(WSHshell,"System Error:Could not get Wscript.Shell ActiveXObject",2);
//
var doList=verb_=="";
//
var isDir=objFSO.FolderExists(tgt_);
if (isDir) {
DoForFolder();
}else{
DoForFile();
}
WScript.Quit(0);// EXIT OK
// functions...
// redundant code prequel
function DoForFile(){
var objFile = objFSO.GetFile(tgt_);
QuitIfNull(objFile,"Error:Path \""+tgt_+"\" Not Found",3);
//
var folder = shellApp.NameSpace(objFSO.GetParentFolderName(objFile));
QuitIfNull(folder,"System Error:Could not get parent folder name of \""+tgt_+"\"",3);
//
var folderItem=folderItemFromFolder(tgt_,folder);
QuitIfNull(folderItem,"System Error:Could not folder item from parent folder of \""+tgt_+"\"",3);
//
if (doList) {
listItemVerbs(folderItem);
return;
}
var folderItemVerb=folderItemVerbFromFileItem(verb_,folderItem);
QuitIfNull(folderItemVerb,"System Error:Could not folder item verb from folder item \""+tgt_+"\"",3);
//
runTheVerb(folderItemVerb);
}
// redundant code redux
function DoForFolder(){
var folder = shellApp.NameSpace(tgt_);
QuitIfNull(folder,"System Error:Could not folder of \""+tgt_+"\"",2);
//
var folderItem=folder.Self;
QuitIfNull(folderItem,"System Error:Could not folder item of \""+tgt_+"\"",2);
//
if (doList) {
listItemVerbs(folderItem);
return;
}
var folderItemVerb=folderItemVerbFromFileItem(verb_,folderItem);
QuitIfNull(folderItemVerb,"System Error:Could not folder item verb from folder item \""+tgt_+"\"",3);
//
runTheVerb(folderItemVerb);
}
// HACK!! this part sucks, have to remain open else dialog dies
function runTheVerb(verb){
verb.DoIt();
WSH.Sleep(5000);
var focus_way=false;
if (focus_way) {
while (WSHshell.AppActivate("Properties")) {WSH.Sleep(2000)};
}else{ // forever way
WSH.Sleep(60*60*1000);// sleep for 60 minutes !
}
}
// match path and return folder item
function folderItemFromFolder(path,folder){
var folderItems = new Enumerator(folder.Items());
for (; ! folderItems.atEnd(); folderItems.moveNext()) {
var folderItem = folderItems.item();
if (folderItem.Path==tgt_) {
//WScript.Echo("tgt_:"+folderItem.Path);
//WScript.Quit(0);
return folderItem
}
}
return null;
}
// match verb name
function folderItemVerbFromFileItem(verb,folderItem){
verb=verb.replace('&','');
var folderItemVerbs = new Enumerator(folderItem.Verbs());
for (; ! folderItemVerbs.atEnd(); folderItemVerbs.moveNext()) {
var folderItemVerb = folderItemVerbs.item();
//WScript.echo("Verb:"+folderItemVerb );
var fix_n=folderItemVerb.Name.replace('&','');
if (fix_n == verb) {
return folderItemVerb;
}
}
return null;
}
// echo the verbs, ignore blanks, remove ampersands
function listItemVerbs(folderItem){
var folderItemVerbs = new Enumerator(folderItem.Verbs());
for (; ! folderItemVerbs.atEnd(); folderItemVerbs.moveNext()) {
var folderItemVerb = folderItemVerbs.item();
var fix_n=folderItemVerb.Name.replace('&','');
if (fix_n!="") {
WScript.echo(""+fix_n );
}
}
}
function QuitIfNull(who,why,how){
if (who==null) {
QuitMsg(why,how);
}
}
function QuitMsg(why,how){
WScript.Echo(why);
WScript.Quit(how);
}

Less CSS and local storage issue

I'm using LESS CSS (more exactly less.js) which seems to exploit LocalStorage under the hood. I had never seen such an error like this before while running my app locally, but now I get "Persistent storage maximum size reached" at every page display, just above the link the unique .less file of my app.
This only happens with Firefox 12.0 so far.
Is there any way to solve this?
P.S.: mainly inspired by Calculating usage of localStorage space, this is what I ended up doing (this is based on Prototype and depends on a custom trivial Logger class, but this should be easily adapted in your context):
"use strict";
var LocalStorageChecker = Class.create({
testDummyKey: "__DUMMY_DATA_KEY__",
maxIterations: 100,
logger: new Logger("LocalStorageChecker"),
analyzeStorage: function() {
var result = false;
if (Modernizr.localstorage && this._isLimitReached()) {
this._clear();
}
return result;
},
_isLimitReached: function() {
var localStorage = window.localStorage;
var count = 0;
var limitIsReached = false;
do {
try {
var previousEntry = localStorage.getItem(this.testDummyKey);
var entry = (previousEntry == null ? "" : previousEntry) + "m";
localStorage.setItem(this.testDummyKey, entry);
}
catch(e) {
this.logger.debug("Limit exceeded after " + count + " iteration(s)");
limitIsReached = true;
}
}
while(!limitIsReached && count++ < this.maxIterations);
localStorage.removeItem(this.testDummyKey);
return limitIsReached;
},
_clear: function() {
try {
var localStorage = window.localStorage;
localStorage.clear();
this.logger.debug("Storage clear successfully performed");
}
catch(e) {
this.logger.error("An error occurred during storage clear: ");
this.logger.error(e);
}
}
});
document.observe("dom:loaded",function() {
var checker = new LocalStorageChecker();
checker.analyzeStorage();
});
P.P.S.: I didn't measure the performance impact on the UI yet, but a decorator could be created and perform the storage test only every X minutes (with the last timestamp of execution in the local storage for instance).
Here is a good resource for the error you are running into.
http://www.sitepoint.com/building-web-pages-with-local-storage/#fbid=5fFWRXrnKjZ
Gives some insight that localstorage only has so much room and you can max it out in each browser. Look into removing some data from localstorage to resolve your problem.
Less.js persistently caches content that is #imported. You can use this script to clear content that is cached. Using the script below you can call the function destroyLessCache('/path/to/css/') and it will clear your localStorage of css files that have been cached.
function destroyLessCache(pathToCss) { // e.g. '/css/' or '/stylesheets/'
if (!window.localStorage || !less || less.env !== 'development') {
return;
}
var host = window.location.host;
var protocol = window.location.protocol;
var keyPrefix = protocol + '//' + host + pathToCss;
for (var key in window.localStorage) {
if (key.indexOf(keyPrefix) === 0) {
delete window.localStorage[key];
}
}
}

Resources