Tuesday, January 27, 2015

Create Liferay plugin with Maven (Liferay Portal 6.2)

Step 1:
Download Liferay Portal Maven 6.2.1 CE.
Extract and run: ant install

Step 2:
Download liferay-plugin-maven-support 6.2.2.
Extract and run: mvn clean install

Step 3:
Create Liferay plugin in Liferay IDE with maven build type.
Add pom.xml, like below:

<?xml version="1.0"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.plugins</groupId>
    <artifactId>test-service</artifactId>
    <packaging>war</packaging>
    <name>test-service Portlet</name>
    <version>1.0.0-SNAPSHOT</version>
    <properties>
        <liferay.version>6.2.1</liferay.version>
    </properties>
    <build>
        <pluginManagement>
            <plugins>
                <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
                <plugin>
                    <groupId>org.eclipse.m2e</groupId>
                    <artifactId>lifecycle-mapping</artifactId>
                    <version>1.0.0</version>
                    <configuration>
                        <lifecycleMappingMetadata>
                            <pluginExecutions>
                                <pluginExecution>
                                    <pluginExecutionFilter>
                                        <groupId>
                                            com.liferay.maven.plugins
                                        </groupId>
                                        <artifactId>
                                            liferay-maven-plugin
                                        </artifactId>
                                        <versionRange>
                                            [6.2.1,)
                                        </versionRange>
                                        <goals>
                                            <goal>build-service</goal>
                                        </goals>
                                    </pluginExecutionFilter>
                                    <action>
                                        <ignore></ignore>
                                    </action>
                                </pluginExecution>
                            </pluginExecutions>
                        </lifecycleMappingMetadata>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>com.liferay.maven.plugins</groupId>
                <artifactId>liferay-maven-plugin</artifactId>
                <version>${liferay.maven.plugin.version}</version>
                <executions>
                    <execution>
                        <id>build-service</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>build-service</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <autoDeployDir>${liferay.auto.deploy.dir}</autoDeployDir>
                    <appServerDeployDir>${liferay.app.server.deploy.dir}</appServerDeployDir>
                    <appServerLibGlobalDir>${liferay.app.server.lib.global.dir}</appServerLibGlobalDir>
                    <appServerPortalDir>${liferay.app.server.portal.dir}</appServerPortalDir>
                    <liferayVersion>${liferay.version}</liferayVersion>
                    <pluginType>portlet</pluginType>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>portal-service</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>util-bridges</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>util-taglib</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>util-java</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.portlet</groupId>
            <artifactId>portlet-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
<profiles>
<profile>
    <id>Liferay-v6.2-CE-(Tomcat-7)</id>
    <properties>
        <liferay.version>6.2.1</liferay.version>
        <liferay.maven.plugin.version>6.2.1</liferay.maven.plugin.version>
        <liferay.auto.deploy.dir>D:\Liferay\Soft\Liferay 6.2\liferay-portal-6.2-ce-ga2\deploy</liferay.auto.deploy.dir>
        <liferay.app.server.deploy.dir>D:\Liferay\Soft\Liferay 6.2\liferay-portal-6.2-ce-ga2\tomcat-7.0.42\webapps</liferay.app.server.deploy.dir>
        <liferay.app.server.lib.global.dir>D:\Liferay\Soft\Liferay 6.2\liferay-portal-6.2-ce-ga2\tomcat-7.0.42\lib\ext</liferay.app.server.lib.global.dir>
        <liferay.app.server.portal.dir>D:\Liferay\Soft\Liferay 6.2\liferay-portal-6.2-ce-ga2\tomcat-7.0.42\webapps\ROOT</liferay.app.server.portal.dir>
    </properties>
    </profile>
</profiles>
</project>


Now, you can run generate-sources to build Liferay service.
Important: Liferay IDE must fully support Maven plugin type. Should download the newest version.

Monday, January 19, 2015

Override session in Liferay

With the same user login in the same browser, you open up a page and working on it. This page uses session to store some data. If you open another tab, enter the same page with the previous tab and doing it, the session in new tab may override the old stored by the previous tab. So the data may be incorrect. Be careful!

WebDAV client tool to upload file to dotCMS server

Sometimes, you cannot use window's Map network drive to connect to dotCMS webDAV, CyberDuct is an alternative. Get it from: https://cyberduck.io/?l=en, window version.

After installing, open it, new a connection and specify needed value:

Type: WebDAV
Server: localhost
Port: 8080
Username: admin@dotcms.com
Password: admin

Click "More Option":
Input Path: /webdav/autopub

Tuesday, January 13, 2015

Lucene search

Maven dependencies:

        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-common</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-queryparser</artifactId>
            <version>4.0.0</version>
        </dependency>


Create IndexWriterFactory:

public enum IndexWriterFactory {
    WRITER;

    private static IndexWriter indexWriter = null;

    public IndexWriter getIndexWriter() throws IOException {

        ConfigUtil systemConfig = ConfigUtil.getInstance();
        String directoryConfig = systemConfig.getValue("index.directory");
        FSDirectory directory = null;

        boolean isLocked = new File(directoryConfig, IndexWriter.WRITE_LOCK_NAME).exists();
        if (isLocked) {
            return null;
        }
        File indexDirectory = new File(directoryConfig);

        StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);
        IndexWriterConfig indexConfig = new IndexWriterConfig(Version.LUCENE_40, analyzer);

        try {
            NativeFSLockFactory lock = new NativeFSLockFactory();
            directory = NIOFSDirectory.open(indexDirectory, lock);

            indexWriter = new IndexWriter(directory, indexConfig);
        } catch (IOException e) {
            e.printStackTrace();
            throw (e);
        }
        return indexWriter;
    }

    public void deleteAll() throws IOException {
        indexWriter.deleteAll();
        indexWriter.commit();
    }

    public void close() throws IOException {
        indexWriter.close();
    }
}

Create IndexReaderFactory:

public enum IndexReaderFactory {
    READER;

    private static IndexReader indexReader = null;

    public static IndexReader getIndexReader() throws IOException {
        ConfigUtil config = ConfigUtil.getInstance();
        String directoryConfig = config.getValue("index.directory");
        try {
            File indexDirectory = new File(directoryConfig);
            FSDirectory directory = FSDirectory.open(indexDirectory);
            indexReader = DirectoryReader.open(directory);
        } catch (IOException e) {
            e.printStackTrace();
            throw (e);
        }
        return indexReader;
    }
}

Create your index implementation:

public enum IndexServiceImpl {
    INDEXER;

    public Set<YourModel> indexItemQueue = null;

    private static Logger LOGGER = LoggerFactory.getLogger(IndexAssets.class);
    {
        indexItemQueue = new LinkedHashSet<YourModel>();
    }

    public void addIndexItem(YourModel indexItem) {
        indexItemQueue.add(indexItem);
    }

    public void addIndexItem(List<YourModel> indexItemList) {
        indexItemQueue.addAll(indexItemList);
    }

    public synchronized String reIndex() throws Exception {
        LOGGER.debug("BEGIN: index " + indexItemQueue.size());
        IndexWriter indexWriter = IndexWriterFactory.WRITER.getIndexWriter();

        String status = null;
        int count = 0;
        if (indexWriter != null) {
            YourModel entry = null;
            Iterator<YourModel> iterator = indexItemQueue.iterator();
            try {
                while (iterator.hasNext()) {
                    entry = iterator.next();
                    iterator.remove();
                    Document document = new Document();
                    Term term = null;


                    //add index fields to document
                    document.add(new StringField("field_name", entry.getValue(), Field.Store.YES));


                    //create term to query to check if this index is existed or not
                    term = new Term("field_name", entry.getValue());
                    //
                    // Query for term above
                    //
                    if(term != null){
                        indexWriter.updateDocument(term, document);
                    } else {
                        indexWriter.addDocument(document);
                    }
                }
            } finally {
                indexWriter.close();
            }
            LOGGER.debug("END: index " + count + " records");
            status = "INDEX_FINISHED";
            return status;

        } else {
            status = "INDEX_UNFINISHED";
            return status;
        }
    }


public synchronized void reIndexAll() throws Exception {
        //delete all index data first
        IndexAssets.INDEXER.deleteAll();
       
        //checkout if deleting process done, every 5s
        try {
            IndexReader reader = IndexAssets.getIndexReader();
            int numOfDoc = reader.numDocs();
            while (0 != numOfDoc) {
                Thread.sleep(5000);
                numOfDoc = reader.numDocs();
            }
        } catch (InterruptedException e) {
            LOGGER.error("There is an exeception  when deleting all old index items. Cause:" + e.getMessage());
        } catch (Exception e) {
            LOGGER.error("There is an exeception  when deleting all old index items. Cause:" + e.getMessage());
        }
       
        List<YourModel> list = new ArrayList<YourModel>();
        list.addAll(/*your data here*/);
       
        LOGGER.info("INDEXING: " + list.size() + " records");
       
        IndexAssets.INDEXER.addIndexItem(list);
        IndexAssets.INDEXER.index();
    }


    public String deleteAll() throws Exception {
        String status = null;
        indexItemQueue.clear();
        IndexWriter indexWriter = IndexWriterFactory.WRITER.getIndexWriter();
        if (indexWriter != null) {
            IndexWriterFactory.WRITER.deleteAll();
            indexWriter.close();
            status = "INDEX_FINISHED";
            return status;
        } else {
            status = "INDEX_UNFINISHED";
            return status;
        }
    }
}

Search in index:

public List<String[]> search(String keyword, int pageNum, boolean isSearchExactKeyword) {
        ConfigUtil config = ConfigUtil.getInstance();
        String numberOfProgramPerPageStr = config.getValue("search.numberOfProgramPerPage");
        int numberOfProgramPerPage = Integer.parseInt(numberOfProgramPerPageStr);
        String directoryStr = config.getValue("index.directory");

        // get from config
        int from = (pageNum - 1) * numberOfProgramPerPage;

        List<String[]> results = new ArrayList<String[]>();

        //escape special characters
        keyword = keyword.replaceAll("/^\\s+|\\s$/g", " ").toLowerCase();


        String[] keywords_Array = keyword.split(" ");
        Set<String> stopwordsSet = new HashSet<String>();

        //remove out the stopword from the keyword
        Iterator iter = StandardAnalyzer.STOP_WORDS_SET.iterator();
        while (iter.hasNext()) {
            char[] stopWord = (char[]) iter.next();
            stopwordsSet.add(new String(stopWord));
        }

        String[] stopwordsArray = new String[stopwordsSet.size()];
        stopwordsSet.toArray(stopwordsArray);
        BooleanQuery bq = new BooleanQuery();
        boolean isStopword = false;
        for (String key : keywords_Array) {
            isStopword = false;
            for (String stopword : stopwordsArray) {
                if (key.toLowerCase().equals(stopword)) {
                    isStopword = true;
                    break;
                }
            }
            if (!isStopword) {

                Query query = new TermQuery(new Term("field_name", key));

                if (isSearchExactKeyword ==  true) {
                    bq.add(query, BooleanClause.Occur.MUST);

                } else {// for program search
                    bq.add(query, BooleanClause.Occur.SHOULD);

                }
               
            }
        }
        try {
            IndexReader reader = IndexReaderFactory.INDEXREADER.getIndexReader();
            IndexSearcher searcher = new IndexSearcher(reader);

            TotalHitCountCollector collectorCount = new TotalHitCountCollector();
            searcher.search(bq, collectorCount);
            int count = collectorCount.getTotalHits();

            if (count <= 0) {
                LOGGER.debug("No Result");
                return results;
            }
            TopFieldCollector collector = TopFieldCollector.create(Sort.RELEVANCE, count, true, false, false, true);
            searcher.search(bq, collector);
           
            ScoreDoc[] hits = new ScoreDoc[0];
            if (pageNum == -1) {
                hits = collector.topDocs().scoreDocs;
            } else {
                hits = collector.topDocs(from, numberOfProgramPerPage).scoreDocs;
                // If found nothing, return search from page 0;
                if (hits.length <= 0) {
                    from = 0;
                    hits = collector.topDocs(from, numberOfProgramPerPage).scoreDocs;
                }
            }
      
            int sequenceNum= 0;
            String sequenceNumString = "";

            for (int i = 0; i < hits.length; i++) {
                int docId = hits[i].doc;
                Document doc = null;
                try {
                    doc = searcher.doc(docId);
                } catch (IOException e) {
                    e.printStackTrace();
                }

                //get out the data from document
                String fieldData_1 = doc.get("field_name1");
                String fieldData_2 = doc.get("field_name2");


                sequenceNum++;
                sequenceNumString = String.valueOf(sequenceNum);

                results.add(new String[] { fieldData_1, fieldData_2, sequenceNumString });
            }
            reader.close();


        } catch (IOException e) {
            e.printStackTrace();
        }
        return results;
    } 

Deeper understand of Enum in Java

Reference:
http://howtodoinjava.com/2012/12/07/guide-for-understanding-enum-in-java/
Enumerations (in general) are generally a set of related constants. Each constant represents a specific instance of its Enumerations class. For example:

public enum DIRECTION {
     EAST,
     WEST,
     NORTH,
     SOUTH        //optionally can end with ";"
 }
 
Here EAST, WEST, NORTH and SOUTH are final static inner classes of Direction of type Direction extends java.lang.Enum.

Enums are comparable and serializable implicitly. Also, all enum types in java are singleton by default. So, you can compare enum types using ‘==’ operator.

You can give define your own constructors to initialize the state of enum types.
enum Direction {
    // Enum types
    EAST(0), WEST(180), NORTH(90), SOUTH(270);
 
    // Constructor
    private Direction(final int angle) {
        this.angle = angle;
    }
 
    // Internal state
    private int angle;
 
    public int getAngle() {
        return angle;
    }
}
 
 
Use template methods in enum:
 
package enumTest;
public enum Direction {
    // Enum types
    EAST(0) {
        <strong>@Override</strong>
        public void shout() {
            System.out.println("Direction is East !!!");
        }
    },
    WEST(180) {
        @Override
        public void shout() {
            System.out.println("Direction is West !!!");
        }
    },
    NORTH(90) {
        @Override
        public void shout() {
            System.out.println("Direction is North !!!");
        }
    },
    SOUTH(270) {
        @Override
        public void shout() {
            System.out.println("Direction is South !!!");
        }
    };
    // Constructor
    private Direction(final int angle) {
        this.angle = angle;
    }
 
    // Internal state
    private int angle;
 
    public int getAngle() {
        return angle;
    }
 
    <strong>// Abstract method which need to be implemented</strong>
    public abstract void shout();
}
 
 

Monday, January 12, 2015

DateTime plicker



1.    Introduction

Alloy UI has been supporting DatePicker widget but it does not have ability to choose time. That’s why we need to find another widget that meets our requirement. The timepicker addon adds a timepicker to jQuery UI Datepicker, thus the datepicker and slider components (jQueryUI) are required for using any of these. In addition all datepicker options are still available through the timepicker addon

2. Usage.

2.1. Option


The timepicker does inherit all options from datepicker. However, there are many options that are shared by them both, and many timepicker only options.

2.1.1. Localization Options


currentText
Default: "Now", A Localization Setting - Text for the Now button.
closeText
Default: "Done", A Localization Setting - Text for the Close button.
amNames
Default: ['AM', 'A'], A Localization Setting - Array of strings to try and parse against to determine AM.
pmNames
Default: ['PM', 'P'], A Localization Setting - Array of strings to try and parse against to determine PM.
timeFormat
Default: "HH:mm", A Localization Setting - String of format tokens to be replaced with the time.See Formatting.
timeSuffix
Default: "", A Localization Setting - String to place after the formatted time.


timeOnlyTitle
Default: "Choose Time", A Localization Setting - Title of the wigit when using only timepicker.
timeText
Default: "Time", A Localization Setting - Label used within timepicker for the formatted time.
hourText
Default: "Hour", A Localization Setting - Label used to identify the hour slider.
minuteText
Default: "Minute", A Localization Setting - Label used to identify the minute slider.
secondText
Default: "Second", A Localization Setting - Label used to identify the second slider.
millisecText
Default: "Millisecond", A Localization Setting - Label used to identify the millisecond slider.
microsecText
Default: "Microsecond", A Localization Setting - Label used to identify the microsecond slider.
timezoneText
Default: "Timezone", A Localization Setting - Label used to identify the timezone slider.
isRTL
Default: false, A Localization Setting - Right to Left support.

2.1.2. Alt Field Options


altFieldTimeOnly
Default: true - When altField is used from datepicker altField will only receive the formatted time and the original field only receives date.
altSeparator
Default: (separator option) - String placed between formatted date and formatted time in the altField.
altTimeSuffix
Default: (timeSuffix option) - String always placed after the formatted time in the altField.
altTimeFormat
Default: (timeFormat option) - The time format to use with the altField.

2.1.3. Timezone Options

 

timezoneList
Default: [generated timezones] - An array of timezones used to populate the timezone select. Can be an array of values or an array of objects: { label: "EDT", value: -240 }. The value should be the offset number in minutes. So "-0400" which is the format "-hhmm", would equate to -240 minutes.

2.1.4. Time Field Options.

controlType

Default: 'slider' - Whether to use 'slider' or 'select'. If 'slider' is unavailable through jQueryUI, 'select' will be used. For advanced usage you may pass an object which implements "create", "options", "value" methods to use controls other than sliders or selects. See the _controls property in the source code for more details.

showHour

Default: null - Whether to show the hour control. The default of null will use detection from timeFormat.

showMinute

Default: null - Whether to show the minute control. The default of null will use detection from timeFormat.

showSecond

Default: null - Whether to show the second control. The default of null will use detection from timeFormat.

showMillisec

Default: null - Whether to show the millisecond control. The default of null will use detection from timeFormat.

showMicrosec

Default: null - Whether to show the microsecond control. The default of null will use detection from timeFormat.

showTimezone

Default: null - Whether to show the timezone select.

showTime

Default: true - Whether to show the time selected within the datetimepicker.

stepHour

Default: 1 - Hours per step the slider makes.

stepMinute

Default: 1 - Minutes per step the slider makes.

stepSecond

Default: 1 - Seconds per step the slider makes.

stepMillisec

Default: 1 - Milliseconds per step the slider makes.

stepMicrosec

Default: 1 - Microseconds per step the slider makes.

hour

Default: 0 - Initial hour set.

minute

Default: 0 - Initial minute set.

second

Default: 0 - Initial second set.

millisec

Default: 0 - Initial millisecond set.

microsec


Default: 0 - Initial microsecond set. Note: Javascript's native Date object does not natively support microseconds. Timepicker adds ability to simply Date.setMicroseconds(m) and Date.getMicroseconds(). Date comparisons will not acknowledge microseconds. Use this only for display purposes.

timezone

Default: null - Initial timezone set. This is the offset in minutes. If null the browser's local timezone will be used. If you're timezone is "-0400" you would use -240. For backwards compatibility you may pass "-0400", however the timezone is stored in minutes and more reliable.

hourMin

Default: 0 - The minimum hour allowed for all dates.

minuteMin

Default: 0 - The minimum minute allowed for all dates.

secondMin

Default: 0 - The minimum second allowed for all dates.

millisecMin

Default: 0 - The minimum millisecond allowed for all dates.

microsecMin

Default: 0 - The minimum microsecond allowed for all dates.

hourMax

Default: 23 - The maximum hour allowed for all dates.

minuteMax

Default: 59 - The maximum minute allowed for all dates.

secondMax

Default: 59 - The maximum second allowed for all dates.

millisecMax

Default: 999 - The maximum millisecond allowed for all dates.

microsecMax

Default: 999 - The maximum microsecond allowed for all dates.

hourGrid

Default: 0 - When greater than 0 a label grid will be generated under the slider. This number represents the units (in hours) between labels.

minuteGrid

Default: 0 - When greater than 0 a label grid will be generated under the slider. This number represents the units (in minutes) between labels.

secondGrid

Default: 0 - When greater than 0 a label grid will be genereated under the slider. This number represents the units (in seconds) between labels.

millisecGrid

Default: 0 - When greater than 0 a label grid will be genereated under the slider. This number represents the units (in milliseconds) between labels.

microsecGrid

Default: 0 - When greater than 0 a label grid will be genereated under the slider. This number represents the units (in microseconds) between labels.

 

2.1.5. Other Options

showButtonPanel

Default: true - Whether to show the button panel at the bottom. This is generally needed.

timeOnly

Default: false - Hide the datepicker and only provide a time interface

onSelect

Default: null - Function to be called when a date is chosen or time has changed (parameters: datetimeText, datepickerInstance).

alwaysSetTime

Default: true - Always have a time set internally, even before user has chosen one.

separator

Default: " " - When formatting the time this string is placed between the formatted date and formatted time.

 


2.2. Formatting .


The default format is "HH:mm". To use 12 hour time use something similar to: "hh:mm tt". When both "t" and lower case "h" are present in the timeFormat, 12 hour time will be used.
H
Hour with no leading 0 (24 hour)
HH
Hour with leading 0 (24 hour)
h
Hour with no leading 0 (12 hour)
hh
Hour with leading 0 (12 hour)
m
Minute with no leading 0
mm
Minute with leading 0
s
Second with no leading 0
ss
Second with leading 0
l
Milliseconds always with leading 0
c
Microseconds always with leading 0
t
a or p for AM/PM
T
A or P for AM/PM
tt
am or pm for AM/PM
TT
AM or PM for AM/PM
z
Timezone as defined by timezoneList
Z
Timezone in Iso 8601 format (+04:45)

3. Example

3.1. Basic Initializations

Add a simple datetimepicker to jQuery UI's datepicker
$('#basic_example_1').datetimepicker();


Add only a timepicker:
$('#basic_example_2').timepicker();

Format the time:
$('#basic_example_3').datetimepicker({
         timeFormat: "hh:mm tt"
});

3.2. Using Timezones

Simplest timezone usage:
$('#timezone_example_1').datetimepicker({
         timeFormat: 'hh:mm tt z'
});

Define your own timezone options:
$('#timezone_example_2').datetimepicker({
         timeFormat: 'HH:mm z',
         timezoneList: [ 
                          { value: -300, label: 'Eastern'}, 
                          { value: -360, label: 'Central' }, 
                          { value: -420, label: 'Mountain' }, 
                          { value: -480, label: 'Pacific' } 
                 ]
});

You may also use timezone string abbreviations for values. This should be used with caution. Computing accurate javascript Date objects may not be possible when trying to retrieve or set the date from timepicker (see setDate and getDate examples below). For simple input values however this should work.
$('#timezone_example_3').datetimepicker({
         timeFormat: 'HH:mm z',
         timezone: 'MT',
         timezoneList: [ 
                          { value: 'ET', label: 'Eastern'}, 
                          { value: 'CT', label: 'Central' }, 
                          { value: 'MT', label: 'Mountain' }, 
                          { value: 'PT', label: 'Pacific' } 
                 ]
});

3.2. Slider Modifications

Add a grid to each slider:
$('#slider_example_1').timepicker({
         hourGrid: 4,
         minuteGrid: 10,
         timeFormat: 'hh:mm tt'
});

Set the interval step of sliders:
$('#slider_example_2').datetimepicker({
         timeFormat: 'HH:mm:ss',
         stepHour: 2,
         stepMinute: 10,
         stepSecond: 10
});

Add sliderAccess plugin for touch devices
$('#slider_example_3').datetimepicker({
         addSliderAccess: true,
         sliderAccessArgs: { touchonly: false }
});

Use dropdowns instead of sliders. By default if slider is not available dropdowns will be used.
$('#slider_example_4').datetimepicker({
         controlType: 'select',
         timeFormat: 'hh:mm tt'
});