I am creating my first application in Phonegap. I have made some plugin like FileOpener, Downloader and stausBarNotification. Plugins are like this:
FileOpener.js is like this:
cordova.define("cordova/plugin/fileopener",
function(require, exports, module) {
var exec = require("cordova/exec");
var FileOpener = function () {};
FileOpener.prototype.open = function(url) {
exec(null, null, "FileOpener", "openFile", [url]);
};
var fileOpener = new FileOpener();
module.exports = fileOpener;
});
/**
* Load Plugin
*/
if (!window.plugins) {
window.plugins = {};
}
if (!window.plugins.fileOpener) {
window.plugins.fileOpener = cordova.require("cordova/plugin/fileopener");
}
Downloader.js is like this:
cordova.define("cordova/plugin/downloader",
function(require, exports, module) {
var exec = require("cordova/exec");
var Downloader = function () {};
Downloader.prototype.downloadFile = function(fileUrl, dirName, fileName, overwrite, win, fail) {
navigator.notification.alert('url: '+fileUrl+' to dir: '+dirName + ' to file: '+fileName);
if (overwrite == false)
overwrite = "false";
else
overwrite = "true";
exec(win, fail, "Downloader", "downloadFile", [ fileUrl, dirName, fileName, overwrite ]);
};
var downloader = new Downloader();
module.exports = downloader;
});
if (!window.plugins) {
window.plugins = {};
}
if (!window.plugins.downloader) {
window.plugins.downloader = cordova.require("cordova/plugin/downloader");
}
And I have have made some java file to implement is as well. Now it is running as expected in android device. But now I want a same project for for ios and I want build.phonegap.com will do that for me.
I have include the plugin in config.xml like this:
<gap:plugin name="FileOpener" value="com.phonegap.plugins.fileOpener.FileOpener"/>
<gap:plugin name="Downloader" value="com.phonegap.plugins.downloader.Downloader"/>
<gap:plugin name="StatusBarNotification" value="com.phonegap.plugins.statusBarNotification.StatusBarNotification"/>
Now where to go from here? I have read some articles which is telling me to submit the plugin to phonegap.com how will i do this? Will the project run same as android device in iPhone? Thanks for any help in advance.
In order to submit your plugin, follow the documentation found at https://build.phonegap.com/docs/plugins-contributing. Once submitted, it will be reviewed and made available to the public. You can see a list of available plugins at https://build.phonegap.com/plugins
Related
I made a nice folder structure with the use of require and require in require in javascript. As the require in require needs all functions to be included again I was wondering if there are easier ways to do this.
var file2 = require('./file2.js');
var IncludeAll={
func1: function(msg){
return file2.function1(msg);
},
func2: function(msg){
return file2.function2(msg);
}
};
module.exports = IncludeAll;
You can create a exporter script "exporter.js" file like below.
// exporter.js
module.exports = {
File2: require('./file2'),
File3: require('./file3')
}
Then you can import and call like this.
const {File2} = require('./exporter')
const param = 5;
File2.func1(param);
File2.func2(param);
I found the file plugin could not work.
My function like this:
function (filename, fileObj, succCallback) {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {
fs.root.getFile(filename, {create: true, exclusive: false}, function(fileEntry){
fileEntry.file(function (file) {
var reader = new FileReader();
reader.onloadend = function() {
if(typeof succCallback === 'function') {
succCallback(this.result);
}
};
reader.readAsText(file);
}, myfile.errorHandler);
}, errorHandler);
}, errorHandler);
}
The function do "window.requestFileSystem" succ, but fs.root.getFile not fire the succ/fail function.
I try to debug the "CDVFile.m", and I found the Objective-C Code had be exec over.
the getFile in "CDVFile.m":
- (void)getFile:(CDVInvokedUrlCommand*)command
{
NSString* baseURIstr = [command argumentAtIndex:0];
CDVFilesystemURL* baseURI = [self fileSystemURLforArg:baseURIstr];
NSString* requestedPath = [command argumentAtIndex:1];
NSDictionary* options = [command argumentAtIndex:2 withDefault:nil];
NSObject<CDVFileSystem> *fs = [self filesystemForURL:baseURI];
CDVPluginResult* result = [fs getFileForURL:baseURI requestedPath:requestedPath options:options];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
AS above, the file plugin (source code "DirectoryEntry.js") succCallback(func win) had not be fire, Neither nor failCallback(func fail). the "DirectoryEntry.js":
DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
argscheck.checkArgs('sOFF', 'DirectoryEntry.getFile', arguments);
var fs = this.filesystem;
var win = successCallback && function(result) {
var FileEntry = require('./FileEntry');
var entry = new FileEntry(result.name, result.fullPath, fs, result.nativeURL);
successCallback(entry);
};
var fail = errorCallback && function(code) {
errorCallback(new FileError(code));
};
exec(win, fail, "File", "getFile", [this.toInternalURL(), path, options]);
};
My XCode version: 7.3 (with iOS SDK 9.3)
My Cordova version : 6.5.0
cordova-plugin-file 4.3.2 "File"
My config.xml had added the preference:
<preference name="iosPersistentFileLocation" value="Library" />
<preference name="iosExtraFilesystems" value="library,library-nosync,documents,documents-nosync,cache,bundle,root" />
Have anyone got the same problem, and resolve it? Or give me some tips, thanks!
2017-04-27 additional remarks:
The debug png why no callback fired
I found why no callback fired. But I didnot known why it is "succss and NO_RESULT"...
A few days ago, I found that, Because the other one js file conflict. If I add the js file, the file plugin cannot work. And I have no choice but to make the js file in the iframe page. It can work now.
Perhaps you have the same problem, wish this can help others.
I'm writing a web app with Ionic framework and I'm trying to manage a record and play sounds mechanism. I'm using the following snippet as a service:
.factory('MediaSrv', function ($q, $ionicPlatform, $window) {
var service = {
loadMedia: loadMedia,
getStatusMessage: getStatusMessage,
getErrorMessage: getErrorMessage
};
function loadMedia (src, onError, onStatus, onStop) {
var defer = $q.defer();
$ionicPlatform.ready(function () {
var mediaSuccess = function () {
if (onStop) { onStop(); }
};
var mediaError = function (err) {
_logError(src, err);
if (onError) { onError(err); }
};
var mediaStatus = function (status) {
if (onStatus) { onStatus(status); }
};
if ($ionicPlatform.is('android')) {
src = '/android_asset/www/' + src;
}
defer.resolve(new $window.Media(src, mediaSuccess, mediaError, mediaStatus));
});
return defer.promise;
}
...
return service;
});
I'm able to play an existing .mp3 file, but I cannot record on a non-existing file. I thought it would create the file by itself if the file wasn't found. How can I create an empty .mp3 file for recording?
Creating a file is a server function. You would need a node server using fs to create a file.
From Ionic's website:
Think of Ionic as the front-end UI framework that handles all of the look and feel and UI interactions your app needs in order to be compelling. Kind of like "Bootstrap for Native," but with support for a broad range of common native mobile components, slick animations, and beautiful design.
Ionic can use the Cordova plugins since its built on top of it.
You can use the media-capture plugin to capture audio, however I have found these record as AMR files.
From the [documentation][1]:
// capture callback
var captureSuccess = function(mediaFiles) {
var i, path, len;
for (i = 0, len = mediaFiles.length; i < len; i += 1) {
path = mediaFiles[i].fullPath;
// do something interesting with the file
}
};
// capture error callback
var captureError = function(error) {
navigator.notification.alert('Error code: ' + error.code, null, 'Capture Error');
};
// start audio capture
navigator.device.capture.captureAudio(captureSuccess, captureError, {limit:2});
[1]: http://docs.phonegap.com/en/edge/cordova_media_capture_capture.md.html
I created plugin for send json data in json file.
But I don't understand why send my object json in pipe, and not write file directly in my plugin.
I want use my plugin whit this syntax:
gulp.task('js-hash', function()
{
// Get all js in redis
gulp.src('./build/js/**/*.js')
.pipe(getHashFile('/build/js/'))
.pipe(gulp.dest('./build/js/hash.json'));
});
And not that:
gulp.task('js-hash', function()
{
// Get all js in redis
gulp.src('./build/js/**/*.js')
.pipe(getHashFile('./build/js/hash.json', '/build/js/'));
});
This is my plugin:
var through = require('through2');
var gutil = require('gulp-util');
var crypto = require('crypto');
var fs = require('fs');
var PluginError = gutil.PluginError;
// Consts
const PLUGIN_NAME = 'get-hash-file';
var json = {};
function getHashFile(filename, basename)
{
if (!filename) {
throw PluginError(PLUGIN_NAME, "Missing filename !");
}
// Creating a stream through which each file will pass
var stream = through.obj(function (file, enc, callback) {
if (file.isNull()) {
this.push(file); // Do nothing if no contents
return callback();
}
if (file.isBuffer()) {
var hash = crypto.createHash('sha256').update(String(file.contents)).digest('hex');
json[file.path.replace(file.cwd+basename, '')] = hash;
return callback();
}
if (file.isStream()) {
this.emit('error', new PluginError(PLUGIN_NAME, 'Stream not supported!'));
return callback();
}
}).on('finish', function () {
fs.writeFile(filename, JSON.stringify(json), function(err) {
if (err) {
throw err;
}
});
});
// returning the file stream
return stream;
}
// Exporting the plugin main function
module.exports = getHashFile;
Your are idea
Nothing prevents you from doing this... besides not respecting plugins guidelines!
Users actually assume a plugin will stream files and that they can pipe them to other plugins.
If I get your code right, you're trying to generate a file that contains all sha hashes of inbound files. Why not let users take this file and pipe it to other plugins? You'd be surprised what people could do.
While this question looks a bit opinion-based, you could definitely put the focus on how to deal with files that may not belong to the main stream of files. Issues like this can be found in many plugins; for example, gulp-uglify authors are wondering how they can add source-maps without mixing js and source map downstream.
I wrote JavaScript library to use FileSaver.js and its associated libraries. However, I don't want to always load FileSaver.js whenever someone wants to use my library. And I don't want to force them to load all the various FileSaver related JavaScript libraries with script tags themselves (or even load one of mine which would do that).
Instead, what I'd prefer is something like this. When they call my createImage function, it first does the following:
function createImage(image, name) {
if (typeof(saveAs) !== 'function') {
var element = document.createElement('script');
element.async = false;
element.src = 'FileSaver.js';
element.type = 'text/javascript';
(document.getElementsByTagName('head')[0]||document.body).appendChild(element);
}
// now do the saveImage code
}
Problem is, after the above, the saveAs function is still not defined. It's only after my createImage completes is the saveAs function finally defined.
the Holistic solution is to use a module system. AMD is (in-my-just-an-observation-please-dont-start-a-holy-war-opinion) probably the most commonly used system for browser async code loading. AMD is just a spec, but something like require.js is a very popular tool for using AMD modules.
The idea being that you can define dependencies between your modules, and require.js will go fetch them if need be. The general idea is to mimic the import/namespace functionality of other languages (like java, C#, or python). "code sharing" i think is the term?
simply put you have all your code in a callback function that runs once the dependencies are loaded, so you can be sure the needed objects and methods are present.
update 2015
just an addendum. while the info above is still correct, front end code management is moving quickly toward solutions like Webpack and Browserify, which bundle and concatenate code of any module type and both have dynamic code loading capabilities (webpack calls this code splitting). That coupled with the exponential growth of npm for dependency management is beginning to make AMD less relevant.
Alright, what you need to do is listen for the script to finish loading. Unfortunately there are some bugs with this code for ie<7.
This is the way Mootools Asset.javascript loads scripts and calls a callback when its complete:
var loadScript = function (source, properties) {
properties || (properties = {});
var script = document.createElement('script');
script.async = true;
script.src = source;
script.type = 'text/javascript';
var doc = properties.document || document, load = properties.onload || properties.onLoad;
return delete properties.onload, delete properties.onLoad, delete properties.document,
load && (script.addEventListener ? script.addEventListener("load", load) : script.attachEvent("readystatechange", function() {
[ "loaded", "complete" ].indexOf(this.readyState) >= 0 && load.call(this);
})), script.set(properties).appendChild(doc.head);
}
Now in loadImage you can load the file library as follows:
function createImage(image, name) {
function createImg() {
// now do the saveImage code
}
if (typeof(saveAs) !== 'function') {
loadScript("FileSaver.js", {onLoad: createImg});//load library
}
else {
createImg();
}
}
Should work on most browsers.
Use Head.js: http://headjs.com/
It will load scripts on demand.
So I agree with the AMD comment (can't put code blocking into comments meh...)
Here's what I do for FileSaver.js
First in my requirejs config / main.js :
(function() {
// REMEMBER TO DUPLICATE CHANGES IN GRUNTFILE.JS
requirejs.config({
paths: {
"jquery": "PATH/jquery.min", // NO .js
"lib.filesaver" : "PATH/FileSaver", // NO .js
"shim.blob" : "PATH/Blob" // NO .js
},
shim: {
"lib.filesaver": {deps: ["shim.blob"]}
}
});
define([
"jquery"
], function(
$
) {
$(document).ready(function() {
// start up code...
});
return {};
});
})();
Then I place the Blob.js/jquery and Filersaver in correct places
I also created a IEShim for pre IE10
define([], function () {
/**
* #class IEshims
* container for static IE shim functions
*/
var IEShims = {
/**
* saveFile, pops up a built in javascript file as a download
* #param {String} filename, eg doc.csv
* #param {String} filecontent eg "this","is","csv"
*/
saveAs: function (filename, filecontent, mimetype ) {
var w = window.open();
var doc = w.document;
doc.open( mimetype,'replace');
doc.charset = "utf-8";
doc.write(filecontent);
doc.close();
doc.execCommand("SaveAs", null, filename);
}
};
return IEShims;
});
And lastly when I want to use Filesaver make it required (along with IEShim for bad browsers)
define([
"lib.filesaver",
"IEShims"
],
function (
FileSaver, // it's empty, see saveAs global var
IEShims
) {
...
var fileName = "helloworld.txt";
var fileContents = "Me haz file contents, K Thx Bye";
var mimeType = "text/plain";
if(saveAs) {
var blob = new Blob(
[fileContents],
{type: mimeType + ";charset=" + document.characterSet}
);
saveAs(blob, fileName);
} else {
IEShims.saveAs(fileName, fileContents,mimeType );
}
...
};
The simplest answer is to put your code in the onload handler of the script tag you create:
<script>
var firstScript = document.getElementsByTagName('script')[0],
js = document.createElement('script');
js.src = 'https://cdnjs.cloudflare.com/ajax/libs/Snowstorm/20131208/snowstorm-min.js';
js.onload = function () {
// do stuff with your dynamically loaded script
snowStorm.snowColor = '#99ccff';
};
firstScript.parentNode.insertBefore(js, firstScript);
</script>
Loading scripts dynamically this way is done by Facebook.