Cibul Tech Blog - Fiddling with PHP, javascript and things

Archive for ‘Uncategorized’

Phonegap – iOS push notifications

Tuesday, March 12th, 2013

This tutorial is inspired from this one. A few changes were made to have things working with the 2.5 version of cordova.

Attention: The push notification can only work on a real device, not a simulator.

Steps:

  • What is the push notification?
  • How does it work?
  • Create a certificate.
  • Create a Cordova 2.5 project.
  • Integrate ios-phonegap-plugin to the project.
  • Add some native code.
  • Add some JavaScript to use the push notification API.
  • Server side

What is the push notification?

If we ask wikipedia

The Apple Push Notification Service is a service created by Apple Inc. that was launched together with iOS 3.0 on June 17, 2009… not really interesting

If we ask Apple

It’s something who gonna revolutionize our phone … our life… the world!!

So, what is realy the push notification?

Push notification allows an app to notify you of new messages or events without the need to actually open the application, similar to how a text message will make a sound and pop up on your screen. This is a great way for apps to interact with us in the background, whether it be a game notifying us of some event occurring in our game world or simply the iPad’s mail application beeping as a new message appears in our inbox.

How does it work??

 

  1. An app enables push notifications. The user has to confirm that he wishes to receive these notifications.
  2. The app receives a “device token”. You can think of the device token as the address that push notifications will be sent to.
  3. The app sends the device token to your server.
  4. When something of interest to your app happens, the server sends a push notification to the Apple Push Notification Service, or APNS for short.
  5. APNS sends the push notification to the user’s device.

Create a certificate.

Is it the first time you create a certificate for iOS? No? So you can move to the next section, just don’t forget to enable “Apple Push Notification service”.

In this case the certificate is used secure the connection between the server and the application. To make sure that no one else can send notifications to your applications.

You can use this great tutorial to create a certificate, without forgetting to, create the ck.pem file, we will use this later.

Create a Cordova 2.5 project.

If you don’t already have a Cordova project on your xcode, you can follow the getting started guide of Cordova to create one.

Integrate ios-phonegap-plugin to the project.

After downloading the plugin from github, rename the bin folder to PushNotification, and drop it on the xcode project, then select Create groups for any added folders.

Drag and drop the plugin folder to the xcode project.

Now, from the finder, copy the PushNotification.js file to your www folder (or under a sub folder of www).

Then, add a script tag in your index.html to refer to it.

<script type="text/javascript" src="js/PushNotification.js"></script>

And finaly, and an entry to your config.xml file, underadd this

 

Add some native code.

First of all, because cordova2.5 does not include the JSONkit anymore, we have to make same changes.

Open the PushNotification.m

In the header, change the #import to #import

In the didReceiveRemoteNotification method, change the

NSString *jsStatement = [NSString stringWithFormat:@"window.plugins.pushNotification.notificationCallback(%@);", [userInfo cdvjk_JSONString]];

To

NSString *jsStatement = [NSString stringWithFormat:@"window.plugins.pushNotification.notificationCallback(%@);", [userInfo JSONString]];

Add the following code to your AppDelegate.m class before the @end to handle the notification events

#pragma PushNotification delegation

- (void)application:(UIApplication*)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken

{

PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];

[pushHandler didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];

}

- (void)application:(UIApplication*)app didFailToRegisterForRemoteNotificationsWithError:(NSError*)error

{

PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];

[pushHandler didFailToRegisterForRemoteNotificationsWithError:error];

}

- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo

{

PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];

NSMutableDictionary* mutableUserInfo = [userInfo mutableCopy];

// Get application state for iOS4.x+ devices, otherwise assume active

UIApplicationState appState = UIApplicationStateActive;

if ([application respondsToSelector:@selector(applicationState)]) {

appState = application.applicationState;

}

[mutableUserInfo setValue:@"0" forKey:@"applicationLaunchNotification"];

if (appState == UIApplicationStateActive) {

[mutableUserInfo setValue:@"1" forKey:@"applicationStateActive"];

[pushHandler didReceiveRemoteNotification:mutableUserInfo];

} else {

[mutableUserInfo setValue:@"0" forKey:@"applicationStateActive"];

[mutableUserInfo setValue:[NSNumber numberWithDouble: [[NSDate date] timeIntervalSince1970]] forKey:@"timestamp"];

[pushHandler.pendingNotifications addObject:mutableUserInfo];

}

}

And finally, into the didFinishLaunchingWithOptions method, add this code before the return YES to handle opening the application from a push notification

NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];

if(userInfo) {

PushNotification *pushHandler = [self.viewController getCommandInstance:@"PushNotification"];

NSMutableDictionary* mutableUserInfo = [userInfo mutableCopy];

[mutableUserInfo setValue:@"1" forKey:@"applicationLaunchNotification"];

[mutableUserInfo setValue:@"0" forKey:@"applicationStateActive"];

[pushHandler.pendingNotifications addObject:mutableUserInfo];

}

Add some JavaScript to use the push notification API.

Add on onResume listener next to your ondeviceready listener, to handle the case when the application receives a push message while it is running in the background.

document.addEventListener('resume', this.onResume, false);

and put the onResume function next to onDevice ready

onResume: function(){

var pushNotification = window.plugins.pushNotification;

pushNotification.getPendingNotifications(function(notifications) {

console.warn('getPendingNotifications:%o', notifications);

var notifs = JSON.stringify(notifications.notifications);

if(notifs!='[]'){

navigator.notification.alert(JSON.stringify(notifications.notifications[0].aps.alert));

document.getElementById("log").innerHTML = document.getElementById("log").innerHTML + "
"
+ JSON.stringify(notifications.notifications[0].aps.alert) ;

}

});

}

In the on device ready, add the following code to handle, the case of the application recive a push when she is active, register the application to the APNs server, and the case where the application is closed while received a push

document.addEventListener('push-notification', function(event) {

//alert(event);

console.warn('push-notification!:%o', event.notification.aps.alert);

document.getElementById("log").innerHTML = document.getElementById("log").innerHTML + "
"
+ JSON.stringify(event.notification.aps.alert) ;

navigator.notification.alert(JSON.stringify(event.notification.aps.alert)); //console.log('push-notification!:%o'+ event.push-notification);

//navigator.notification.alert(JSON.stringify(['push-notification!', event]));

});

var pushNotification = window.plugins.pushNotification;

pushNotification.registerDevice({alert:true, badge:true, sound:true}, function(status) {

// if successful status is an object that looks like this:

// {"type":"7","pushBadge":"1","pushSound":"1","enabled":"1","deviceToken":"blablahblah","pushAlert":"1"}

console.warn('registerDevice:%o', status);

console.log(status);

//navigator.notification.alert(JSON.stringify(['registerDevice', status]));

});

pushNotification.getPendingNotifications(function(notifications) {

console.warn('getPendingNotifications:%o', notifications);

// navigator.notification.alert(JSON.stringify(['getPendingNotifications', notifications]));

});

app.receivedEvent('deviceready');

Server side

You can use the simple php script from this link, it’s just to to send requests to the APNS server.

Test on a real device

Posted in Uncategorized | No Comments »

CibulCalendar – A javascript date range picker

Sunday, March 10th, 2013

Overview

CibulCalendar is a date picker that enables users to easily pick dates or date ranges.

You can download the latest version by clicking here or visit the github repo. The minified version of the script is around 10.9kb.

Here is a demo that shows how to select a range with a mouse drag&drop or a touch and drag gesture:


function onload() {
  setCibulCalendar('basic'); // will stick to element <input type="text" id="basic"/>
}

It is also used in the header of the website cibul.net. It works on most major modern browser and IE 8.0+ as well as Android and iOS devices.

Features include:

  • Drag and drop range selection
  • Single date selection
  • Multilingual
  • Helper function for single line setup
  • Fully Customizable (see details below)

How to use

There are two ways to use the calendar:

  • setCibulCalendar(id, options): simplest setup for use with an input field.
  • var cCal = new CibulCalendar(canvasElem, options): more flexible approach to handle date selections as you wish through a callback and keep a reference to the calendar for later use.

Options

  • range (default: true): if false, range date picking is disabled. Here is an example.
  • enabled (default: true): state of the calendar at init
  • lang (default: ‘en’): Language of the calendar. can be english (en), french (fr), italian (it) or spanish (es). You can extend this with any other language (see below).
  • firstDayOfWeek (default: 1): first day of the week on the calendar. 0 for sunday and onwards.
  • selected (default: false): default selection at initialization. Should be a object: {begin: Date, end: Date}
  • filter: (default: false): callback called on each calendar render to handle date classes. If set, function is given a date and an array of classes and should return the array of classes that will be set on the rendered calendar. Click here for an example highlighting weekends.
  • navDomContent (default: {prev: ‘<', next: '>‘}): html content for the month navigation links. In case you want to set something more fancy like icons or images.
  • classes: in case you want to change the name of the classes used in the calendar.
  • monthNames: add a new month set in the language of your choice. See here for an example of a calendar in icelandic
  • weekDays: add a new week set in the language of your choice
  • switchMonthOnHoverDelay: on a range selection spanning over multiple months, the delay during which next month days should be hovered over before the calendar goes to the neighboring month

Methods

  • disable: disable the calendar
  • enable: enable the calendar
  • showNext: render following month
  • showPrevious: render previous month
  • setSelected(newSelection, updateMonth): set new selection. newSelection is an object containing two indexes: begin and end, each referring to a date. updateMonth is a boolean indicating if the month of the start of the selection should be displayed.

Posted in Uncategorized | No Comments »

Upload and display an image without a page refresh

Saturday, February 23rd, 2013

Here we will see how to upload an image without a page refresh using an iframe and some javascript.

There are a bunch of jquery plugins that do this, but you’ll see here that there is nothing really complicated about it and it can easily be achieved with a couple of javascript lines.

Principle

Normally when you want to upload an image to the server you create a post form with a ‘file’ input to allow the user to pick the image, and a submit button for posting the contents to the server. The user clicks on ‘Choose file’, picks his image and clicks on submit… and sees the browser waiting for a full page response after the file is uploaded.

The trick to avoid this full page refresh is to use a ‘target’ attribute on the form and have it point to an iframe. As the submit button is clicked, the browser will load up the response in the targeted iframe without refreshing the whole page.

Try it with the following code. First, the form:

<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <form method="post" action="upload.php" enctype="multipart/form-data" target="leiframe">
      <input type="file" name="image"/>
      <input type="submit" value="upload"/>
    </form>
    <iframe name="leiframe" width="200" height="200"></iframe>
  </body>
</html>

Pretty simple.. Note the target attribute with the name of the iframe

Second, the upload script:

<?php

 // I'm skipping a lloooot of verification steps on the file here.
 // Also, I'm assuming you uploaded a jpg.


 move_uploaded_file($_FILES['image']['tmp_name'], 'uploaded/pic.jpg');
?>
<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <img style="width: 100%;" src="uploaded/pic.jpg?v=<?php echo rand(0,1000) // rand() prevents the browser from displaying a previously cached image ?>"/>
  </body>
</html>

Nothing more.

Already with this, you have a basic solution which uploads and displays images without a page refresh, and without having event started writing any javascript.

the form

Pure eye candy.

But with javascript it is easy to come up with a more flexible solution in only a few steps:

  1. First, we’ll have a page both serving up the form and processing it. Normally two different resources handle this to avoid reposts, but we’ll keep it simple for now.
  2. Then, we prepare a javascript function that handles the iframe creation and form tweaking to make a sleeker image submission
  3. That function will change the destination of the form post, to a script which we write to process the image and prepare the content for the iframe.
  4. And finally, we write a function to handle the response

A regular form submission

The following form submits the image and serves up the result directly with a full refresh:

<?php
 // Process image
 $uploadName = 'uploaded/pic.jpg';
 if (count($_FILES)) move_uploaded_file($_FILES['image']['tmp_name'], $uploadName);
?>
<!DOCTYPE html>
<html>
  <head>
    <style>
      li { display: inline-block; padding: 0.2em; width: 2em;}
      img { width: 100%; }
    </style>
  </head>
  <body onload="onload()">
    <form method="post" action="index.php" enctype="multipart/form-data">
      <input type="file" name="image"/>
      <input type="submit" value="upload"/>
    </form>
    <ul>
      <?php if (count($_FILES)): ?>
      <li><img src="<?php echo $uploadName ?>"/></li>
      <?php endif; ?>
    </ul>
    <script type="text/javascript">
      function onload(){}
    </script>
  </body>
</html>

index.php

Your form works now fine on its own, and reloads itself with whatever image was submitted.

The js function enabling the iframe

Now we need a function that replicates the behavior described earlier in this tutorial: it needs to create an iframe, and have the form point to it through a target attribute. The function will also divert the submission to a dedicated php script that will generate the contents of the iframe. Here it is (don’t forget to add a reference to it in your main file (<script type="text/javascript" src="iframeSubmit.js"></script>:

function iframeSubmit(formElem, action, callback) {

  // we name a callback that will be called from inside the iframe
  var callbackName = 'iframe' + Math.ceil(Math.random()*10000);

  action = action + (action.indexOf('?')==-1?'?':'&') + 'callback=' + callbackName;

  // we create an iframe and use the callback as its name (why not).
  var iframe = document.createElement('iframe');
  iframe.setAttribute('name', callbackName);
  iframe.style.display = 'none';

  // we add the target and edit the action of the form
  formElem.setAttribute('target', callbackName);
  formElem.setAttribute('action', action);

  // we add the hidden iframe after the form
  formElem.parentNode.appendChild(iframe);

  window[callbackName] = callback;
};

Preparing the response for the iframe

When we use the iframeSubmit function, we’ll have the form point to upload.php for image submissions and the result will be loaded within the iframe (which is now a hidden element in the page). The upload.php script should process the submission and generate an iframe content which will call the function defined and passed by iframeSubmit, with the results of the operation. The function will be called from within the iframe, which is possible only when the page and iframes have the same domain origin. Here it is:

<?php

 // I'm skipping a lloooot of verification steps on the file here.
 // Also, I'm assuming you uploaded a jpg.

 $imagePath = 'uploaded/pic.jpg';
 move_uploaded_file($_FILES['image']['tmp_name'], $imagePath);

?>
<!DOCTYPE html>
<html>
  <head>Iframe content</head>
  <body>
    <script type="text/javascript">
      <?php echo 'window.parent[\'' . $_GET['callback'] . '\'](' . json_encode(array(
       'success' => true,
        'path' => $imagePath
      )) . ')' ?>
    </script>
  </body>
</html>

There. Once the response of upload.php is loaded in the iframe, it will only contain the execution of the callback defined in the iframeSubmit script

Wrapping it up

The only thing missing now is the bit in the index page where the javascript function is called and the response processed. Add this to the end of the index page:

<script type="text/javascript">

      function processResponse(response) {
        if (!response.success) {
          alert('woopsie');
          return;
        }

        var newListItem = document.createElement('li');
        newListItem.innerHTML = '<img src="' + response.path + '"/>';

        document.getElementsByTagName('ul')[0].appendChild(newListItem);
      }

      function onload() {

        iframeSubmit(document.getElementsByTagName('form')[0], 'upload.php', processResponse);

      }

    </script>

There, you can put in the callback of the iframeSubmit function all the image handling you want! (The function here only piles up the images received in a list)

the form

Just wow.

Posted in Uncategorized | 3 Comments »

Javascript function for a slide effect on pan/slide gestures or mouse drags

Wednesday, December 19th, 2012

Here is a function that transitions a pane element to a new one by using either a drag of your mouse or a pan/slide gesture on touch enabled devices.

Give it a try if you are looking for a simple solution to handle pane transition or a tab navigation system without using a fully featured tab navigation script.

Drag the pane to the left, or wait for the transition to be triggered


Basic usage

Pass it the element you want to transition from, specifying the elements you want to transition to, as such:

  slide(paneElement, {right: newPaneElement});

The function returns a trigger function, should you want to call the transition by yourself.

You can define several slide directions with different destination panes by just passing them in the second parameter like this:

  slide(paneElement, {right: newRightPaneElement, left: newLeftPaneElement, top: newTopPaneElement, bottom: newBottomPaneElement });

Once the transition is complete, all but the displayed element are removed from the dom.

You’ll find a simple demo page here and a github library here


Compatibility

This was tested with IE8, chrome, firefox, opera and mobile webkit browsers. Write me a note if you have a browser (a recent-ish one) that throws errors

Posted in Uncategorized | 1 Comment »

Phonegap – Using the iOS LocalNotification plugin

Wednesday, November 21st, 2012

The last post was about deploying the plugin offering the same functionality on and android device, here is how to get the iOS version running. Find the github page of the plugin here. There’s also a more detailed procedure here

  1. Get a basic phonegap project running
  2. Get the plugin files. Copy the LocalNotification.h and .m in your project’s Plugins xcode folder and the plugin js file in the www folder.
  3. If you are using cordova 1.7 or above, the content of LocalNotification.h should be something like:
    //
    //  LocalNotification.h
    //  Phonegap LocalNotification Plugin
    //  Copyright (c) Greg Allen 2011 & 2012 Drew Dahlman
    //  MIT Licensed

    #import <Foundation/Foundation.h>

    #ifdef CORDOVA_FRAMEWORK
    #import <Cordova/CDVPlugin.h>
    #else
    #import "CDVPlugin.h"
    #endif

    @interface LocalNotification : CDVPlugin {
       
    }
    - (void)addNotification:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
    - (void)cancelNotification:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
    - (void)cancelAllNotifications:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;

    @end
  4. Open AppDelegate.m in the Classes folder and add this:

    // ADD OUR NOTIFICATION CODE
    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
    {

        UIApplicationState state = [application applicationState];
        if (state == UIApplicationStateActive) {
        // WAS RUNNING
          NSLog(@"I was currently active");

          NSString *notCB = [notification.userInfo objectForKey:@"foreground"];
          NSString *notID = [notification.userInfo objectForKey:@"notificationId"];

          NSString * jsCallBack = [NSString
                                   stringWithFormat:@"%@(%@)", notCB,notID];  


          [self.viewController.webView  stringByEvaluatingJavaScriptFromString:jsCallBack];

          application.applicationIconBadgeNumber = 0;
        }
        else {
            // WAS IN BG
            NSLog(@"I was in the background");

            NSString *notCB = [notification.userInfo objectForKey:@"background"];
            NSString *notID = [notification.userInfo objectForKey:@"notificationId"];

          NSString * jsCallBack = [NSString
                                   stringWithFormat:@"%@(%@)", notCB,notID];
            [self.viewController.webView stringByEvaluatingJavaScriptFromString:jsCallBack];        

            application.applicationIconBadgeNumber = 0;
        }                
    }
  5. Open the Cordova.plist file, and add an entry in the Plugins dictionary. Key: LocalNotification, type: String and value: LocalNotification.
  6. Setup the test project. index.html:

    <!DOCTYPE html>
    <html>
      <head>
      <title></title>
     
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
        <meta charset="utf-8">
        <script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
        <script type="text/javascript" charset="utf-8" src="LocalNotification.js"></script>
        <script type="text/javascript" charset="utf-8" src="init.js"></script>
        <link rel="stylesheet" href="style.css" />
       
      </head>
      <body onload="app.bodyLoad()">
      <div id="header"></div>
        <div class="btn" onClick="notification.local_min();">localNotification + 60 seconds</div>
        <div class="btn" onClick="notification.local_timed(13,30);">localNotification</div>
        <div class="btn" onClick="notification.tomorrow(06,05,1);">localNotification tomorrow</div>
        <div class="btn" onClick="notification.clear();">clear all</div>
      </body>
    </html>

    init.js:

    // JavaScript Document
    // PROJECT: Phonegap LocalNotifications
    // AUTHOR: Drew Dahlman ( www.drewdahlman.com )
    // DATE: 1.26.2012

    /*
    NOTES:
    We will be creating LocalNotifications that can be set to fire while app is inactive,
    and create a callback for the JS to know when the app has come back from a notification.

    One thing that is deceptive about the LocalNotifications plugin is that when it shows a notification
    has been created it shows it based on the timezone +0000 which can throw you off.

    in the call for setting the notification we simply call notification.local_timed("13:00") - note that I supplied a string.

    The ability to set repeating notifications has been added!
    - daily
    - weekly
    - monthly
    - yearly


    */


    // NOTIFICATION CENTER
    /*
    I like to set up one object that's only job is to manage notifications
    */
    var notification = {
        init:function(){
           
        },
       
        // This will fire after 60 seconds
        local_min:function(){
            var d = new Date();
            d = d.getTime() + 60*1000; //60 seconds from now
            d = new Date(d);
            plugins.localNotification.add({
                date: d,
                repeat:'daily',
                message: 'This just fired after a minute!',
                hasAction: true,
                badge: 1,
                id: '1',
                sound:'horn.caf',
                background:'app.background',
                foreground:'app.running'
            });
        },
       
        // This will fire based on the time provided.
        // Something to note is that the iPhone goes off of 24hr time
        // it defaults to no timezone adjustment so +0000 !IMPORTANT
        local_timed:function(hh,mm){
            // the example time we provide is 13:00
            // This means the alarm will go off at 1pm +0000
            // how does this translate to your time? While the phone schedules 1pm +0000
            // it will still go off at your desired time base on your phones time.
           
            console.log(hh+" "+mm);
            // Now lets make a new date
            var d = new Date();
                d = d.setSeconds(00);
                d = new Date(d);
                d = d.setMinutes(mm);
                d = new Date(d);
                d = d.setHours(hh);
                d = new Date(d);
            plugins.localNotification.add({
                date: d,
                repeat:'daily',
                message: 'This went off just as expected!',
                hasAction: true,
                badge: 1,
                id: '2',
                sound:'horn.caf',
                background:'app.background',
                foreground:'app.running'
            });
        },
        clear:function(){
            plugins.localNotification.cancelAll();
        },
        tomorrow:function(hh,mm,days){
            // Now lets make a new date
            var d = new Date();
                d = d.setSeconds(00);
                d = new Date(d);
                d = d.setMinutes(mm);
                d = new Date(d);
                d = d.setHours(hh);
                d = new Date(d);
               
                // add a day
                d = d.setDate(d.getDate()+days);
                d = new Date(d);
               
            plugins.localNotification.add({
                date: d,
                repeat:'daily',
                message: 'This went off just as expected!',
                hasAction: true,
                badge: 1,
                id: '3',
                sound:'horn.caf',
                background:'app.background',
                foreground:'app.running'
            });
        }
       
    }

    // APP
    var app = {
        bodyLoad:function(){
            document.addEventListener("deviceready", app.deviceReady, false);
        },
        deviceReady:function(){
            app.init();
        },
        init:function(){
           
        },
        background:function(id){
            console.log("I was in the background but i'm back now! ID="+id);
        },
        running:function(id){
            console.log("I am currently running, what should I do? ID="+id);
        }
    };

    style.css:

    * {
        margin:0px;
    }
    body {
        font-family:Helvetica;
        -webkit-user-select: none;
        background-color:#fff;
    }
    #wrapper {
        width:100%;
        height:100%;
        padding-top:15px;
    }
    #header {
        height:150px;
        width:100%;
        background-image:url(../images/header.png);
        background-size:100%;
        margin-bottom:10px;
        box-shadow: 0px 0px 2px .6px #222222;
    }
    .btn {
        font-size:18px;
        text-align:center;
        padding-top:12px;
        padding-bottom:12px;
        width:95%; 
        border-radius: 5px;
        background-color:#545454;
        margin:auto;
        color:#fff;
        font-weight:bold;
        box-shadow: 0px 0px 2px .6px #222222;
        margin-top:5px;
    }
  7. Done!

Posted in Uncategorized | No Comments »

Phonegap – Using the Android LocalNotification plugin

Monday, November 5th, 2012

Here is an edit of the readme file found in the LocalNotification plugin for phonegap on Android. I’ve stumbled on a couple of issues which were not covered there and decided to make a version of the step through that worked for me. Assuming you already have a basic project setup (with a 2.0-ish cordova version), follow these steps:

  1. Get a basic phonegap project running for Android.
  2. Add the LocalNotification.js file to your project js files and add a reference in your index.html file. If you are using phonegap 2.0.0 or above, edit it and replace all occurrences of PhoneGap with cordova
  3. Create a package ‘com.phonegap.plugin.localnotification’ (in Eclipse, right click on the project, new > Package)
  4. Copy all plugin java files in your project’s package (drag and drop the files)
  5. Edit AlarmReceiver.java and add import your.project.name.R; in the end of the import list
  6. Edit the LocalNotification.java file by replacing alarm = new AlarmHelper(this.ctx); with alarm = new AlarmHelper(cordova.getContext());
  7. Make sure the package reference in the first line of each of those java files matches the folder you actually placed them in.
  8. Update your res/xml/plugins.xml file with the following line: <plugin name="LocalNotification" value="com.phonegap.plugin.localnotification.LocalNotification" />
  9. Add the following fragment in the AndroidManifest.xml inside the application tag:

    <receiver android:name="com.phonegap.plugin.localnotification.AlarmReceiver" >
    </receiver>

    <receiver android:name="com.phonegap.plugin.localnotification.AlarmRestoreOnBoot" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
  10. Here is example where the notification is shown 3 minutes after the app is run:
    <!DOCTYPE html>
    <html>
      <head>
        <script type="text/javascript" src="cordova-2.0.0.js"></script>
        <script type="text/javascript" src="LocalNotification.js"></script>
      </head>
      <body>
        <script type="text/javascript">
         
          document.addEventListener("deviceready", appReady, false);

          function appReady() {

            if (typeof plugins !== "undefined") {

              var now = new Date();
              now.setSeconds(now.getSeconds() + 90);

              plugins.localNotification.add({
                date : now,
                message : "Phonegap - Boooyyyaaaaah!\r\nUpyoass!",
                ticker : "Yeeeaaaaahhhh!!!",
                repeatDaily : false,
                id : 4
              });
            }
          };

        </script>
      </body>
    </html>
  11. That’s it.

Posted in Uncategorized | 4 Comments »

A render of a Cibul event using the freshly published Cibul Plugin

Friday, July 20th, 2012

With this plugin, put links to cibul events in your posts and they will be converted into event renders like such:

  • 48.870965 2.373952 La Java 105 Rue du Faubourg du Temple, 75010 Paris, France la-java

The template used is customizable, and the css can be modified in the plugin’s admin page. Find the plugin page here.

Posted in Uncategorized | No Comments »

Turn urls into links in text with jQuery

Saturday, December 3rd, 2011

Here is a little function that can be used to turn urls found in text on a page into clickable links. I assembled it and tweaked it taking bits and pieces from several forums.

See it running here

/* doLinks script */

function linkify(inputText, options) {
   
  this.options = {linkClass: 'url', targetBlank: true};

  this.options = $.extend(this.options, options);
 
  inputText = inputText.replace(/\u200B/g, "");

  //URLs starting with http://, https://, or ftp://
  var replacePattern1 = /(src="|href="|">|\s>)?(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;ï]*[-A-Z0-9+&@#\/%=~_|ï]/gim;
  var replacedText = inputText.replace(replacePattern1, function($0,$1){ return $1?$0:'<a class="'+ this.options.linkClass + '" href="' + $0 + '"' + (this.options.targetBlank?'target="_blank"':'') + '>'+ $0+ '</a>';});

  //URLS starting with www and not the above
  var replacePattern2 = /(src="|href="|">|\s>|https?:\/\/|ftp:\/\/)?www\.[-A-Z0-9+&@#\/%?=~_|!:,.;ï]*[-A-Z0-9+&@#\/%=~_|ï]/gim;
  var replacedText = replacedText.replace(replacePattern2, function($0,$1){ return $1?$0:'<a class="'+ this.options.linkClass + '" href="http://' + $0 + '"' + (this.options.targetBlank?'target="_blank"':'') + '>'+ $0+ '</a>';});

  //Change email addresses to mailto:: links
  var replacePattern3 = /([\.\w]+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;
  var replacedText = replacedText.replace(replacePattern3, '<a class="' + this.options.linkClass + '" href="mailto:$1">$1</a>');

  return replacedText;
}

$.fn.doLinks = function(){
  this.each(function(){
    $(this).html(linkify($(this).html()));
  });
}

Just apply .doLinks() on the elements you need to process. Let me know of links that don’t work with this.

Posted in Uncategorized | 10 Comments »

Drag and Drop two Markers linked with a line using Google Maps v3

Friday, July 8th, 2011

In this article we’ll use the google maps v3 API to show two draggable markers on a map linked with a line. As the markers are dragged, we want the line position to be updated so that it appears to always be linked and we also want to an updated indicator displaying the distance between the two markers in kilometers.

To achieve this, we will use the Markers MVCObject properties to bind their positions to a method which will update the line and indicator as the Markers are dragged around the map.

Getting started by creating a Map

We start with an empty html page in which we will write our script. This page shows a full page map with over it a box in which we’ll keep an updated value of the distance between the markers. Here we go:

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <title>Distance widget</title>
    <style type="text/css">
      html, body { height: 100%; margin: 0; padding:0; }
      #map-canvas { height: 100%; width: 100%; }
      #distance-display { position: absolute; bottom: 0; left: 0; width: 100px; height: 20px; z-index: 1; background: white; text-align: center; padding: 10px;}
    </style>
    <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
    <script type"text/javascript">
      $(function(){
       
        var map = new google.maps.Map($('#map-canvas'), {
          center: new google.maps.LatLng(48.860932,2.335925),
          zoom: 13,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        });

      });
    </script>
  </head>
  <body>
    <div id="distance-display">distance</div>
    <div id="map-canvas"></div>
  </body>
</html>

With this we have a working full page map ready to receive some markers and a polyline. The libraries we are using are the google maps v3 (obviously) and the jquery library which we use to handle things on the page (like $(‘#map-canvas’)) and also to ensure that the javascript is not executed before the page is fully loaded (that is what the $(function(){}) is for).

Add two markers and a line

Now we add our two markers. One on the Louvre Museum and the second on the Eiffel Tower. Add this after the map declaration:

        var marker = new google.maps.Marker({
          map: map,
          position: new google.maps.LatLng(48.860932,2.335925),
          draggable: true,
          raiseOnDrag: false
        });

        var marker2 = new google.maps.Marker({
          map: map,
          position: new google.maps.LatLng(48.858221,2.29449),
          draggable: true,
          raiseOnDrag: false
        });

We also create the line, which is a Polyline with only two positions, one for each marker:

        var line = new google.maps.Polyline({
          path: [marker.getPosition(), marker2.getPosition()],
          map: map
        });

Calculate the distance between two markers

Before we start binding the markers with the line, we need to be able to calculate the distance between the markers. There used to be a practical method to do this in a previous version the the google maps API but it does not exist in the latest version (last time I checked anyways). So we will add it to the LatLng class so that we can easily determine the distance between two points. This methods uses the Haversine formula to calculate the distance. Add it before the $(function(){… line:

      google.maps.LatLng.prototype.distanceFrom = function(newLatLng) {
        if (newLatLng==undefined) return false;

        var dLat = (newLatLng.lat()-this.lat()) * Math.PI / 180;
        var dLon = (newLatLng.lng()-this.lng()) * Math.PI / 180;
        var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(this.lat() * Math.PI / 180 ) * Math.cos(newLatLng.lat() * Math.PI / 180 )* Math.sin(dLon/2) * Math.sin(dLon/2);
        return 6371000 * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
      }

With this, we can now simply determine the distance between two points. We apply it on the position of our markers at the end of the script:

$('#distance-display').html(Math.round(marker.getPosition().distanceFrom(marker2.getPosition())/10)/100 + ' km');

Here we are calculating the distance, rounding it up to have something in kilometers and we put it in our display

Tie everything up using MVCObject properties

For the time being, everything is initialized, the two markers are displayed with a line in between them, the distance between the two markers is displayed as well at the bottom of the screen, but nothing happens when one of the markers is dragged. What we need is to update the position of the line and update the distance display as the drag occurs. This is where the MVCObject properties of our objects are useful. Using them, we can bind attributes (in our case, the position of the markers) to methods which will update the line position and the distance display any time the attributes are changed.

First we create the method that will update the line and the display (before the $(function(){ again):

      var update_line_and_display = function(line, display, position1, position2){

        // update the line
        line.getPath().setAt(0, position1);
        line.getPath().setAt(1, position2);
       
        // update the display
       
        $(display).html(Math.round(position1.distanceFrom(position2)/10)/100 + ' km');
      }

We want that function to be called whenever the position of the markers change. As markers inherit the MVCObject, their attributes are bound to methods which if defined are triggered any time the attribute is changed. The names of these methods derive from the attribute. In our case, we use the position_changed method for each marker. We add this at the end of the script and we are done!

        marker.position_changed = function() {
          update_line_and_display(line, '#distance-display', this.get('position'), marker2.getPosition());
        }

        marker2.position_changed = function() {
          update_line_and_display(line, '#distance-display', marker.getPosition(), this.get('position'));
        }

You can see it working here.

Tags: , ,
Posted in Uncategorized | 4 Comments »

Cibul Tech: Fiddling with PHP

Monday, April 12th, 2010

Hi, welcome to the Cibul Tech blog! The purpose of this blog will be to post notes about whatever we think is worthwhile mentioning as we stumble upon them while we try to move forward with building web applications. Personnaly, I am fairly new to developping using PHP and javascript and I have only recently started fiddling with the symfony framework, and as I struggle with the bits and pieces, I find the documentation, tutorials and tips from blogs and other online resources really helpful and so this blog will be my attempt to contribute back to the PHP community.

Anyway, I hope you will find some of the stuff posted here useful, any feedback will be greatly appreciated.

Posted in Uncategorized | No Comments »