Deep learning and ETL for the JVM

Deeplearning4j offers a stack of components for building JVM-based applications that incorporate AI

Eclipse Deeplearning4j is an open source, distributed, deep learning library for the JVM. Deeplearning4j is written in Java and is compatible with any JVM language, such as Scala, Clojure, or Kotlin. The underlying computations are written in C, C++, and Cuda. Keras will serve as the Python API. Integrated with Hadoop and Apache Spark, Deeplearning4j brings AI to business environments for use on distributed GPUs and CPUs.

Deeplearning4j is actually a stack of projects intended to support all the needs of a JVM-based deep learning application. Beyond Deeplearning4j itself (the high-level API), it includes ND4J (general-purpose linear algebra,), SameDiff (graph-based automatic differentiation), DataVec (ETL), Arbiter (hyperparameter search), and the C++ LibND4J (underpins all of the above). LibND4J in turns calls on standard libraries for CPU and GPU support, such as OpenBLAS, OneDNN (MKL-DNN), cuDNN, and cuBLAS.

The goal of Eclipse Deeplearning4j is to provide a core set of components for building applications that incorporate AI. AI products within an enterprise often have a wider scope than just machine learning. The overall goal of the distribution is to provide smart defaults for building deep learning applications.

 

advertisement

 

Deeplearning4j competes, at some level, with every other deep learning framework. The most comparable project in scope is TensorFlow, which is the leading end-to-end deep learning framework for production. TensorFlow currently has interfaces for  Python, C++, and Java (experimental), and a separate implementation for JavaScript. TensorFlow uses two ways of training: graph-based and immediate mode (eager execution). Deeplearning4j currently only supports graph-based execution.

PyTorch, probably the leading deep learning framework for research, only supports immediate mode; it has interfaces for Python, C++, and Java. H2O Sparkling Water integrates the H2O open source, distributed in-memory machine learning platform with Spark. H2O has interfaces for Java and Scala, Python, R, and H2O Flow notebooks.

Commercial support for Deeplearning4j can be purchased from Konduit, which also supports many of the developers working on the project.

How Deeplearning4j works

Deeplearning4j treats the tasks of loading data and training algorithms as separate processes. You load and transform the data using the DataVec library, and train models using tensors and the ND4J library.

You ingest data through a RecordReader interface, and walk through the data using a RecordReaderDataSetIterator. You can choose a DataNormalization class to use as a preprocessor for your DataSetIterator. Use the ImagePreProcessingScaler for image data, the NormalizerMinMaxScaler if you have a uniform range along all dimensions of your input data, and NormalizerStandardize for most other cases. If necessary, you can implement a custom DataNormalization class.

DataSet objects are containers for the features and labels of your data, and keep the values in several instances of INDArray: one for the features of your examples, one for the labels, and two additional ones for masking, if you are using time series data. In the case of the features, the INDArray is a tensor of the size Number of Examples x Number of Features. Typically you’ll divide the data into mini-batches for training; the number of examples in an INDArray is small enough to fit in memory but large enough to get a good gradient.

If you look at the Deeplearning4j code for defining models, such as the Java example below, you’ll see that it’s a very high-level API, similar to Keras. In fact, the planned Python interface to Deeplearning4j will use Keras; right now, if you have a Keras model, you can import it into Deeplearning4j.

MultiLayerConfiguration conf =
    new NeuralNetConfiguration.Builder()
        .optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)
        .updater(new Nesterovs(learningRate, 0.9))
        .list(
            new DenseLayer.Builder().nIn(numInputs).nOut(numHiddenNodes).activation("relu").build(),
            new OutputLayer.Builder(LossFunction.NEGATIVELOGLIKELIHOOD).
                  activation("softmax").nIn(numHiddenNodes).nOut(numOutputs).build()
        ).backprop(true).build();

The MultiLayerNetwork class is the simplest network configuration API available in Eclipse Deeplearning4j; for DAG structures, use the ComputationGraph instead. Note that the optimisation algorithm (SGD in this example) is specified separately from the updater (Nesterov in this example). This very simple neural network has one dense layer with a ReLU activation function and one output layer with -log(likelihood) loss and a softmax activation function, and is solved by back propagation. More complex networks may also have GravesLSTM, ConvolutionLayer, EmbeddingLayer, and others of the two dozen supported layer types and sixteen layer space types.

The simplest way to train the model is to call the .fit() method on the model configuration with your DataSetIterator as an argument. You can also reset the iterator and call the .fit() method for as many epochs as you need, or use an EarlyStoppingTrainer.

To test model performance, use an Evaluation class to see how well the trained model fits your test data, which should not be the same as the training data.

Deeplearning4j provides a listener facility help you monitor your network’s performance visually, which will be called after each mini-batch is processed. One of most often used listeners is ScoreIterationListener.

Installing and testing Deeplearning4j

At the moment, the easiest way to try out Deeplearning4j is by using the official quick start. It requires a relatively recent version of Java, an installation of Maven, a working Git, and a copy of IntelliJ IDEA (preferred) or Eclipse. There are also a few user-contributed quick starts. Start by cloning the eclipse/deeplearning4j-examples repo to your own machine with Git or GitHub Desktop. Then install the projects with Maven from the dl4j-examples folder.

martinheller@Martins-Retina-MacBook dl4j-examples % mvn clean install
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for org.deeplearning4j:dl4j-examples:jar:1.0.0-beta7
[WARNING] 'build.plugins.plugin.(groupId:artifactId)' must be unique but found duplicate declaration of plugin org.apache.maven.plugins:maven-compiler-plugin @ line 250, column 21
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]
[INFO]
[INFO] ------------------< org.deeplearning4j:dl4j-examples >------------------[INFO] Building Introduction to DL4J 1.0.0-beta7[INFO] --------------------------------[ jar ]---------------------------------Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-enforcer-plugin/1.0.1/maven-enforcer-plugin-1.0.1.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-enforcer-plugin/1.0.1/maven-enforcer-plugin-1.0.1.pom (6.5 kB at 4.4 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/enforcer/enforcer/1.0.1/enforcer-1.0.1.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/enforcer/enforcer/1.0.1/enforcer-1.0.1.pom (11 kB at 137 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-enforcer-plugin/1.0.1/maven-enforcer-plugin-1.0.1.jar
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-enforcer-plugin/1.0.1/maven-enforcer-plugin-1.0.1.jar (22 kB at 396 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/mojo/exec-maven-plugin/1.4.0/exec-maven-plugin-1.4.0.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/mojo/exec-maven-plugin/1.4.0/exec-maven-plugin-1.4.0.pom (12 kB at 283 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/mojo/exec-maven-plugin/1.4.0/exec-maven-plugin-1.4.0.jar
Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/mojo/exec-maven-plugin/1.4.0/exec-maven-plugin-1.4.0.jar (46 kB at 924 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/com/lewisd/lint-maven-plugin/0.0.11/lint-maven-plugin-0.0.11.pom
Downloaded from central: https://repo.maven.apache.org/maven2/com/lewisd/lint-maven-plugin/0.0.11/lint-maven-plugin-0.0.11.pom (19 kB at 430 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/com/lewisd/lint-maven-plugin/0.0.11/lint-maven-plugin-0.0.11.jar
Downloaded from central: https://repo.maven.apache.org/maven2/com/lewisd/lint-maven-plugin/0.0.11/lint-maven-plugin-0.0.11.jar (106 kB at 1.6 MB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-compiler-plugin/3.5.1/maven-compiler-plugin-3.5.1.pom

…

[WARNING]   - org.agrona.collections.Hashing
[WARNING]   - org.agrona.collections.Long2ObjectCache$ValueIterator
[WARNING]   - org.agrona.collections.Int2ObjectHashMap$EntrySet
[WARNING]   - org.agrona.concurrent.SleepingIdleStrategy
[WARNING]   - org.agrona.collections.MutableInteger
[WARNING]   - org.agrona.collections.Int2IntHashMap
[WARNING]   - org.agrona.collections.IntIntConsumer
[WARNING]   - org.agrona.concurrent.status.StatusIndicator
[WARNING]   - 175 more...
[WARNING] javafx-base-14-mac.jar, javafx-graphics-14-mac.jar, jakarta.xml.bind-api-2.3.2.jar define 1 overlapping classes:
[WARNING]   - module-info
[WARNING] protobuf-1.0.0-beta7.jar, guava-19.0.jar define 3 overlapping classes:
[WARNING]   - com.google.thirdparty.publicsuffix.TrieParser
[WARNING]   - com.google.thirdparty.publicsuffix.PublicSuffixPatterns
[WARNING]   - com.google.thirdparty.publicsuffix.PublicSuffixType
[WARNING] jsr305-3.0.2.jar, guava-1.0.0-beta7.jar define 35 overlapping classes:
[WARNING]   - javax.annotation.RegEx
[WARNING]   - javax.annotation.concurrent.Immutable
[WARNING]   - javax.annotation.meta.TypeQualifierDefault
[WARNING]   - javax.annotation.meta.TypeQualifier
[WARNING]   - javax.annotation.Syntax
[WARNING]   - javax.annotation.CheckReturnValue
[WARNING]   - javax.annotation.CheckForNull
[WARNING]   - javax.annotation.Nonnull
[WARNING]   - javax.annotation.meta.TypeQualifierNickname
[WARNING]   - javax.annotation.MatchesPattern
[WARNING]   - 25 more...
[WARNING] maven-shade-plugin has detected that some class files are
[WARNING] present in two or more JARs. When this happens, only one
[WARNING] single version of the class is copied to the uber jar.
[WARNING] Usually this is not harmful and you can skip these warnings,
[WARNING] otherwise try to manually exclude artifacts based on
[WARNING] mvn dependency:tree -Ddetail=true and the above output.
[WARNING] See http://maven.apache.org/plugins/maven-shade-plugin/
[INFO] Attaching shaded artifact.
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ dl4j-examples ---[INFO] Installing /Volumes/Data/repos/deeplearning4j-examples/dl4j-examples/target/dl4j-examples-1.0.0-beta7.jar to /Users/martinheller/.m2/repository/org/deeplearning4j/dl4j-examples/1.0.0-beta7/dl4j-examples-1.0.0-beta7.jar
[INFO] Installing /Volumes/Data/repos/deeplearning4j-examples/dl4j-examples/pom.xml to /Users/martinheller/.m2/repository/org/deeplearning4j/dl4j-examples/1.0.0-beta7/dl4j-examples-1.0.0-beta7.pom
[INFO] Installing /Volumes/Data/repos/deeplearning4j-examples/dl4j-examples/target/dl4j-examples-1.0.0-beta7-shaded.jar to /Users/martinheller/.m2/repository/org/deeplearning4j/dl4j-examples/1.0.0-beta7/dl4j-examples-1.0.0-beta7-shaded.jar
[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time:  05:07 min
[INFO] Finished at: 2020-07-10T10:58:55-04:00
[INFO] ------------------------------------------------------------------------martinheller@Martins-Retina-MacBook dl4j-examples %

Once the installation is complete, open the dl4j-examples/ directory with IntelliJ IDEA and try running some of the examples.

The README under dl4j-examples lists all the examples and briefly describes them. By the way, you can use the IntelliJ IDEA preferences to install a new version of the JDK and apply it to the project.
The well-known Iris dataset has just 150 samples and is generally easy to model, although a few of the irises are often misclassified. The model used here is a three-layer dense neural network.
Running the Iris classifier shown in the previous figure yields a fairly good fit: accuracy, precision, recall, and F1 score are all ~98%. Note in the confusion matrix that only one of the test cases was misclassified.
The Linear Classifier demo runs in a few seconds and generates probability plots for the training and test datasets. The data was generated specifically to be linearly separable into two classes.
A multi-layer perceptron (MLP) classification model for the MNIST hand-written digit dataset yields accuracy, precision, recall, and F1 score all ~97% after about 14K iterations. That isn’t as good or as fast as the results of convolutional neural networks (such as LeNet) on this dataset.

Deeplearning4j performance

Benchmarking Java programs can be tricky. In particular, you need to warm up the code before timing it to eliminate the overhead of the JIT compiler, and you need to make sure that the JVM has enough RAM configured for the program being benchmarked. The Deeplearning4j community maintains a repository of benchmark code for a variety of popular models and configurations.

According to the developers, Deeplearning4j is as fast as Caffe for non-trivial image recognition tasks using multiple GPUs. To use multiple machines, you can run Deeplearning4j with Spark.

Deep learning for Java applications

Overall, Deeplearning4j is a worthy library for loading and conditioning data and performing deep learning on the Java VM. While not yet as mature as TensorFlow or PyTorch, Deeplearning4j will appeal to data scientists who want to integrate deep learning models into Java-based applications. Deeplearning4j’s ability to import Keras models will ease the transition for people who want to switch from TensorFlow.

Deeplearning4j supports creating graphs and then running them, like TensorFlow 1. It doesn’t support immediate mode training, like TensorFlow 2 and PyTorch. That doesn’t matter much for production, but it might make using Deeplearning4j less attractive for research.

Deeplearning4j currently has two mechanisms for distributed training, parameter averaging and gradient sharing. The latter is preferable, but it was only added to the library in Version 1.0.0-beta3. Deeplearning4j is also integrated with Spark, which helps to support training on a cluster of machines. Using Spark doesn’t really help when training on a single server with multiple GPUs.

We look forward to Deeplearning4j 1.0 emerging from beta test.

—

Cost: Free open source under the Apache 2.0 license. 

Platform: Windows, macOS, Linux; Java 7 or later and Maven required.

IDG News Service

RELATED ARTICLES
#mc_embed_signup{background:#fff; false;clear:left; font:14px Helvetica,Arial,sans-serif; width: px;} /* Add your own Mailchimp form style overrides in your site stylesheet or in this style block. We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
Sign up for the
Technology Minute
(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';fnames[1]='FNAME';ftypes[1]='text';fnames[2]='MMERGE2';ftypes[2]='text';fnames[3]='MMERGE3';ftypes[3]='text';fnames[4]='MMERGE4';ftypes[4]='text';fnames[5]='SIGNUP';ftypes[5]='text';fnames[6]='MMERGE6';ftypes[6]='imageurl';}(jQuery));var $mcj = jQuery.noConflict(true); // SMS Phone Multi-Country Functionality if(!window.MC) { window.MC = {}; } window.MC.smsPhoneData = { defaultCountryCode: 'IE', programs: [], smsProgramDataCountryNames: [] }; function getCountryUnicodeFlag(countryCode) { return countryCode.toUpperCase().replace(/./g, (char) => String.fromCodePoint(char.charCodeAt(0) + 127397)) }; // HTML sanitization function to prevent XSS function sanitizeHtml(str) { if (typeof str !== 'string') return ''; return str .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, ''') .replace(/\//g, '/'); } // URL sanitization function to prevent javascript: and data: URLs function sanitizeUrl(url) { if (typeof url !== 'string') return ''; const trimmedUrl = url.trim().toLowerCase(); if (trimmedUrl.startsWith('javascript:') || trimmedUrl.startsWith('data:') || trimmedUrl.startsWith('vbscript:')) { return '#'; } return url; } const getBrowserLanguage = () => { if (!window?.navigator?.language?.split('-')[1]) { return window?.navigator?.language?.toUpperCase(); } return window?.navigator?.language?.split('-')[1]; }; // getBrowserLanguageCode() and getSmsProgramForCountryAndLanguage() are provided globally // by the external mc-sms-phone.js script loaded alongside this inline form script. function getDefaultCountryProgram(defaultCountryCode, smsProgramData) { if (!smsProgramData || smsProgramData.length === 0) { return null; } var browserCountryCode = getBrowserLanguage(); var browserLangCode = getBrowserLanguageCode(); if (browserCountryCode) { var match = getSmsProgramForCountryAndLanguage(smsProgramData, browserCountryCode, browserLangCode); if (match) { return match; } } if (defaultCountryCode) { var match = getSmsProgramForCountryAndLanguage(smsProgramData, defaultCountryCode, browserLangCode); if (match) { return match; } } return smsProgramData[0]; } function updateSmsLegalText(countryCode, fieldName) { if (!countryCode || !fieldName) { return; } var programs = window?.MC?.smsPhoneData?.programs; if (!programs || !Array.isArray(programs)) { return; } var browserLangCode = getBrowserLanguageCode(); var program = getSmsProgramForCountryAndLanguage(programs, countryCode, browserLangCode); if (!program || !program.requiredTemplate) { return; } var smsConsentHtmlRenderingFixEnabled = true; const legalTextElement = document.querySelector('#legal-text-' + fieldName); if (!legalTextElement) { return; } const divRegex = new RegExp(']*>', 'gi'); const blockWrapperRegex = new RegExp(']*>', 'gi'); const fullAnchorRegex = new RegExp('<a.*?', 'g'); const anchorRegex = new RegExp('(.*?)'); const template = smsConsentHtmlRenderingFixEnabled ? program.requiredTemplate .replace(/\s*

]*>/gi, ' ') .replace(blockWrapperRegex, '') : program.requiredTemplate.replace(divRegex, ''); legalTextElement.textContent = ''; const parts = template.split(/(.*?)/g); parts.forEach(function(part) { if (!part) { return; } const anchorMatch = part.match(/(.+?)/); if (anchorMatch) { const linkElement = document.createElement('a'); linkElement.href = sanitizeUrl(anchorMatch[1]); linkElement.target = anchorMatch[2] ? sanitizeHtml(anchorMatch[2]) : '_blank'; linkElement.rel = 'noopener noreferrer'; linkElement.textContent = sanitizeHtml(anchorMatch[3]); legalTextElement.appendChild(linkElement); } else { legalTextElement.appendChild(document.createTextNode(part)); } }); } function generateDropdownOptions(smsProgramData) { if (!smsProgramData || smsProgramData.length === 0) { return ''; } var programs = true ? smsProgramData.filter(function(p, i, arr) { return arr.findIndex(function(q) { return q.countryCode === p.countryCode; }) === i; }) : smsProgramData; return programs.map(program => { const flag = getCountryUnicodeFlag(program.countryCode); const countryName = getCountryName(program.countryCode); const callingCode = program.countryCallingCode || ''; // Sanitize all values to prevent XSS const sanitizedCountryCode = sanitizeHtml(program.countryCode || ''); const sanitizedCountryName = sanitizeHtml(countryName || ''); const sanitizedCallingCode = sanitizeHtml(callingCode || ''); return '' + sanitizedCountryName + ' ' + sanitizedCallingCode + ''; }).join(''); } function getCountryName(countryCode) { if (window.MC?.smsPhoneData?.smsProgramDataCountryNames && Array.isArray(window.MC.smsPhoneData.smsProgramDataCountryNames)) { for (let i = 0; i < window.MC.smsPhoneData.smsProgramDataCountryNames.length; i++) { if (window.MC.smsPhoneData.smsProgramDataCountryNames[i].code === countryCode) { return window.MC.smsPhoneData.smsProgramDataCountryNames[i].name; } } } return countryCode; } function getDefaultPlaceholder(countryCode) { if (!countryCode || typeof countryCode !== 'string') { return '+1 000 000 0000'; // Default US placeholder } var mockPlaceholders = [ { countryCode: 'US', placeholder: '+1 000 000 0000', helpText: 'Include the US country code +1 before the phone number', }, { countryCode: 'GB', placeholder: '+44 0000 000000', helpText: 'Include the GB country code +44 before the phone number', }, { countryCode: 'CA', placeholder: '+1 000 000 0000', helpText: 'Include the CA country code +1 before the phone number', }, { countryCode: 'AU', placeholder: '+61 000 000 000', helpText: 'Include the AU country code +61 before the phone number', }, { countryCode: 'DE', placeholder: '+49 000 0000000', helpText: 'Fügen Sie vor der Telefonnummer die DE-Ländervorwahl +49 ein', }, { countryCode: 'FR', placeholder: '+33 0 00 00 00 00', helpText: 'Incluez le code pays FR +33 avant le numéro de téléphone', }, { countryCode: 'ES', placeholder: '+34 000 000 000', helpText: 'Incluya el código de país ES +34 antes del número de teléfono', }, { countryCode: 'NL', placeholder: '+31 0 00000000', helpText: 'Voeg de NL-landcode +31 toe vóór het telefoonnummer', }, { countryCode: 'BE', placeholder: '+32 000 00 00 00', helpText: 'Incluez le code pays BE +32 avant le numéro de téléphone', }, { countryCode: 'CH', placeholder: '+41 00 000 00 00', helpText: 'Fügen Sie vor der Telefonnummer die CH-Ländervorwahl +41 ein', }, { countryCode: 'AT', placeholder: '+43 000 000 0000', helpText: 'Fügen Sie vor der Telefonnummer die AT-Ländervorwahl +43 ein', }, { countryCode: 'IE', placeholder: '+353 00 000 0000', helpText: 'Include the IE country code +353 before the phone number', }, { countryCode: 'IT', placeholder: '+39 000 000 0000', helpText: 'Includere il prefisso internazionale IT +39 prima del numero di telefono', }, { countryCode: 'NO', placeholder: '+47 000 00 000', helpText: 'Inkluder NO landskode +47 før telefonnummeret', }, { countryCode: 'SE', placeholder: '+46 00 000 00 00', helpText: 'Inkludera SE landskod +46 före telefonnumret', }, { countryCode: 'DK', placeholder: '+45 00 00 00 00', helpText: 'Inkluder DK landekode +45 før telefonnummeret', }, { countryCode: 'FI', placeholder: '+358 00 000 0000', helpText: 'Sisällytä FI-maakoodi +358 ennen puhelinnumeroa', }, { countryCode: 'EE', placeholder: '+372 0000 0000', helpText: 'Lisage EE riigikood +372 telefoninumbri ette', }, { countryCode: 'PL', placeholder: '+48 000 000 000', helpText: 'Podaj numer kierunkowy PL +48 przed numerem telefonu', }, { countryCode: 'SK', placeholder: '+421 000 000 000', helpText: 'Pred telefónne číslo uveďte kód krajiny SK +421', }, { countryCode: 'LV', placeholder: '+371 0000 0000', helpText: 'Iekļaujiet LV valsts kodu +371 pirms tālruņa numura', }, { countryCode: 'LT', placeholder: '+370 0000 0000', helpText: 'Įtraukite LT šalies kodą +370 prieš telefono numerį', }, { countryCode: 'GR', placeholder: '+30 000 000 0000', helpText: 'Συμπεριλάβετε τον κωδικό χώρας GR +30 πριν από τον αριθμό τηλεφώνου', }, { countryCode: 'PT', placeholder: '+351 000 000 000', helpText: 'Inclua o código de país PT +351 antes do número de telefone', }, { countryCode: 'HR', placeholder: '+385 00 000 0000', helpText: 'Uključite HR pozivni broj države +385 prije telefonskog broja', }, { countryCode: 'SI', placeholder: '+386 00 000 000', helpText: 'Vključite SI kodo države +386 pred telefonsko številko', }, { countryCode: 'IS', placeholder: '+354 000 0000', helpText: 'Láttu IS landsnúmer +354 fylgja á undan símanúmerinu', }, { countryCode: 'LU', placeholder: '+352 000 000 000', helpText: 'Incluez le code pays LU +352 avant le numéro de téléphone', }, { countryCode: 'MC', placeholder: '+377 00 00 00 00', helpText: 'Incluez le code pays MC +377 avant le numéro de téléphone', }, { countryCode: 'AD', placeholder: '+376 000 000', helpText: 'Incloeu el codi de país AD +376 abans del número de telèfon', }, { countryCode: 'JE', placeholder: '+44 0000 000000', helpText: 'Include the JE country code +44 before the phone number', }, { countryCode: 'IM', placeholder: '+44 0000 000000', helpText: 'Include the IM country code +44 before the phone number', }, { countryCode: 'GG', placeholder: '+44 0000 000000', helpText: 'Include the GG country code +44 before the phone number', }, { countryCode: 'AL', placeholder: '+355 00 000 0000', helpText: 'Përfshini kodin e vendit AL +355 para numrit të telefonit', }, { countryCode: 'SM', placeholder: '+378 0000 000000', helpText: 'Includere il prefisso internazionale SM +378 prima del numero di telefono', }, { countryCode: 'FO', placeholder: '+298 000000', helpText: 'Inkluder FO landekode +298 før telefonnummeret', }, { countryCode: 'MT', placeholder: '+356 0000 0000', helpText: 'Include the MT country code +356 before the phone number', }, { countryCode: 'LI', placeholder: '+423 000 0000', helpText: 'Fügen Sie vor der Telefonnummer die LI-Ländervorwahl +423 ein', }, { countryCode: 'GI', placeholder: '+350 000 00000', helpText: 'Include the GI country code +350 before the phone number', }, { countryCode: 'MD', placeholder: '+373 00 000 000', helpText: 'Includeți codul de țară MD +373 înaintea numărului de telefon', }, { countryCode: 'HU', placeholder: '+36 00 000 0000', helpText: 'A telefonszám előtt adja meg a HU országkódot +36', }, { countryCode: 'NZ', placeholder: '+64 00 000 0000', helpText: 'Include the NZ country code +64 before the phone number', }, { countryCode: 'ME', placeholder: '+382 00 000 000', helpText: 'Uključite ME pozivni broj države +382 prije telefonskog broja', }, ]; const selectedPlaceholder = mockPlaceholders.find(function(item) { return item && item.countryCode === countryCode; }); return selectedPlaceholder ? selectedPlaceholder.placeholder : mockPlaceholders[0].placeholder; } function updatePlaceholder(countryCode, fieldName) { if (!countryCode || !fieldName) { return; } const phoneInput = document.querySelector('#mce-' + fieldName); if (!phoneInput) { return; } const placeholder = getDefaultPlaceholder(countryCode); if (placeholder) { phoneInput.placeholder = placeholder; } } function updateCountryCodeInstruction(countryCode, fieldName) { updatePlaceholder(countryCode, fieldName); } function getDefaultHelpText(countryCode) { var mockPlaceholders = [ { countryCode: 'US', placeholder: '+1 000 000 0000', helpText: 'Include the US country code +1 before the phone number', }, { countryCode: 'GB', placeholder: '+44 0000 000000', helpText: 'Include the GB country code +44 before the phone number', }, { countryCode: 'CA', placeholder: '+1 000 000 0000', helpText: 'Include the CA country code +1 before the phone number', }, { countryCode: 'AU', placeholder: '+61 000 000 000', helpText: 'Include the AU country code +61 before the phone number', }, { countryCode: 'DE', placeholder: '+49 000 0000000', helpText: 'Fügen Sie vor der Telefonnummer die DE-Ländervorwahl +49 ein', }, { countryCode: 'FR', placeholder: '+33 0 00 00 00 00', helpText: 'Incluez le code pays FR +33 avant le numéro de téléphone', }, { countryCode: 'ES', placeholder: '+34 000 000 000', helpText: 'Incluya el código de país ES +34 antes del número de teléfono', }, { countryCode: 'NL', placeholder: '+31 0 00000000', helpText: 'Voeg de NL-landcode +31 toe vóór het telefoonnummer', }, { countryCode: 'BE', placeholder: '+32 000 00 00 00', helpText: 'Incluez le code pays BE +32 avant le numéro de téléphone', }, { countryCode: 'CH', placeholder: '+41 00 000 00 00', helpText: 'Fügen Sie vor der Telefonnummer die CH-Ländervorwahl +41 ein', }, { countryCode: 'AT', placeholder: '+43 000 000 0000', helpText: 'Fügen Sie vor der Telefonnummer die AT-Ländervorwahl +43 ein', }, { countryCode: 'IE', placeholder: '+353 00 000 0000', helpText: 'Include the IE country code +353 before the phone number', }, { countryCode: 'IT', placeholder: '+39 000 000 0000', helpText: 'Includere il prefisso internazionale IT +39 prima del numero di telefono', }, { countryCode: 'NO', placeholder: '+47 000 00 000', helpText: 'Inkluder NO landskode +47 før telefonnummeret', }, { countryCode: 'SE', placeholder: '+46 00 000 00 00', helpText: 'Inkludera SE landskod +46 före telefonnumret', }, { countryCode: 'DK', placeholder: '+45 00 00 00 00', helpText: 'Inkluder DK landekode +45 før telefonnummeret', }, { countryCode: 'FI', placeholder: '+358 00 000 0000', helpText: 'Sisällytä FI-maakoodi +358 ennen puhelinnumeroa', }, { countryCode: 'EE', placeholder: '+372 0000 0000', helpText: 'Lisage EE riigikood +372 telefoninumbri ette', }, { countryCode: 'PL', placeholder: '+48 000 000 000', helpText: 'Podaj numer kierunkowy PL +48 przed numerem telefonu', }, { countryCode: 'SK', placeholder: '+421 000 000 000', helpText: 'Pred telefónne číslo uveďte kód krajiny SK +421', }, { countryCode: 'LV', placeholder: '+371 0000 0000', helpText: 'Iekļaujiet LV valsts kodu +371 pirms tālruņa numura', }, { countryCode: 'LT', placeholder: '+370 0000 0000', helpText: 'Įtraukite LT šalies kodą +370 prieš telefono numerį', }, { countryCode: 'GR', placeholder: '+30 000 000 0000', helpText: 'Συμπεριλάβετε τον κωδικό χώρας GR +30 πριν από τον αριθμό τηλεφώνου', }, { countryCode: 'PT', placeholder: '+351 000 000 000', helpText: 'Inclua o código de país PT +351 antes do número de telefone', }, { countryCode: 'HR', placeholder: '+385 00 000 0000', helpText: 'Uključite HR pozivni broj države +385 prije telefonskog broja', }, { countryCode: 'SI', placeholder: '+386 00 000 000', helpText: 'Vključite SI kodo države +386 pred telefonsko številko', }, { countryCode: 'IS', placeholder: '+354 000 0000', helpText: 'Láttu IS landsnúmer +354 fylgja á undan símanúmerinu', }, { countryCode: 'LU', placeholder: '+352 000 000 000', helpText: 'Incluez le code pays LU +352 avant le numéro de téléphone', }, { countryCode: 'MC', placeholder: '+377 00 00 00 00', helpText: 'Incluez le code pays MC +377 avant le numéro de téléphone', }, { countryCode: 'AD', placeholder: '+376 000 000', helpText: 'Incloeu el codi de país AD +376 abans del número de telèfon', }, { countryCode: 'JE', placeholder: '+44 0000 000000', helpText: 'Include the JE country code +44 before the phone number', }, { countryCode: 'IM', placeholder: '+44 0000 000000', helpText: 'Include the IM country code +44 before the phone number', }, { countryCode: 'GG', placeholder: '+44 0000 000000', helpText: 'Include the GG country code +44 before the phone number', }, { countryCode: 'AL', placeholder: '+355 00 000 0000', helpText: 'Përfshini kodin e vendit AL +355 para numrit të telefonit', }, { countryCode: 'SM', placeholder: '+378 0000 000000', helpText: 'Includere il prefisso internazionale SM +378 prima del numero di telefono', }, { countryCode: 'FO', placeholder: '+298 000000', helpText: 'Inkluder FO landekode +298 før telefonnummeret', }, { countryCode: 'MT', placeholder: '+356 0000 0000', helpText: 'Include the MT country code +356 before the phone number', }, { countryCode: 'LI', placeholder: '+423 000 0000', helpText: 'Fügen Sie vor der Telefonnummer die LI-Ländervorwahl +423 ein', }, { countryCode: 'GI', placeholder: '+350 000 00000', helpText: 'Include the GI country code +350 before the phone number', }, { countryCode: 'MD', placeholder: '+373 00 000 000', helpText: 'Includeți codul de țară MD +373 înaintea numărului de telefon', }, { countryCode: 'HU', placeholder: '+36 00 000 0000', helpText: 'A telefonszám előtt adja meg a HU országkódot +36', }, { countryCode: 'NZ', placeholder: '+64 00 000 0000', helpText: 'Include the NZ country code +64 before the phone number', }, { countryCode: 'ME', placeholder: '+382 00 000 000', helpText: 'Uključite ME pozivni broj države +382 prije telefonskog broja', }, ]; if (!countryCode || typeof countryCode !== 'string') { return mockPlaceholders[0].helpText; } const selectedHelpText = mockPlaceholders.find(function(item) { return item && item.countryCode === countryCode; }); return selectedHelpText ? selectedHelpText.helpText : mockPlaceholders[0].helpText; } function setDefaultHelpText(countryCode) { const helpTextSpan = document.querySelector('#help-text'); if (!helpTextSpan) { return; } } function updateHelpTextCountryCode(countryCode, fieldName) { if (!countryCode || !fieldName) { return; } setDefaultHelpText(countryCode); } function initializeSmsPhoneDropdown(fieldName) { if (!fieldName || typeof fieldName !== 'string') { return; } const dropdown = document.querySelector('#country-select-' + fieldName); const displayFlag = document.querySelector('#flag-display-' + fieldName); if (!dropdown || !displayFlag) { return; } const smsPhoneData = window.MC?.smsPhoneData; if (smsPhoneData && smsPhoneData.programs && Array.isArray(smsPhoneData.programs)) { dropdown.innerHTML = generateDropdownOptions(smsPhoneData.programs); } const defaultProgram = getDefaultCountryProgram(smsPhoneData?.defaultCountryCode, smsPhoneData?.programs); if (defaultProgram && defaultProgram.countryCode) { dropdown.value = defaultProgram.countryCode; const flagSpan = displayFlag?.querySelector('#flag-emoji-' + fieldName); if (flagSpan) { flagSpan.textContent = getCountryUnicodeFlag(defaultProgram.countryCode); flagSpan.setAttribute('aria-label', sanitizeHtml(defaultProgram.countryCode) + ' flag'); } updateSmsLegalText(defaultProgram.countryCode, fieldName); updatePlaceholder(defaultProgram.countryCode, fieldName); updateCountryCodeInstruction(defaultProgram.countryCode, fieldName); } var smsNotRequiredRemoveCountryCodeEnabled = true; var smsField = Object.values({"EMAIL":{"name":"EMAIL","label":"Email Address","helper_text":"","type":"email","required":true,"audience_field_name":"Email Address","merge_id":0,"help_text_enabled":false,"enabled":true,"order":0,"field_type":"merge"},"FNAME":{"name":"FNAME","label":"First Name","helper_text":"","type":"text","required":false,"audience_field_name":"First Name","enabled":false,"order":null,"field_type":"merge","merge_id":1},"MMERGE2":{"name":"MMERGE2","label":"Second Name","helper_text":"","type":"text","required":false,"audience_field_name":"Second Name","enabled":false,"order":null,"field_type":"merge","merge_id":2},"MMERGE3":{"name":"MMERGE3","label":"Company Name","helper_text":"","type":"text","required":false,"audience_field_name":"Company Name","enabled":false,"order":null,"field_type":"merge","merge_id":3},"MMERGE4":{"name":"MMERGE4","label":"Job Title","helper_text":"","type":"text","required":false,"audience_field_name":"Job Title","enabled":false,"order":null,"field_type":"merge","merge_id":4},"SIGNUP":{"name":"SIGNUP","label":"Signup form location","helper_text":"","type":"text","required":false,"audience_field_name":"Signup form location","enabled":false,"order":null,"field_type":"merge","merge_id":5},"MMERGE6":{"name":"MMERGE6","label":"Image URL","helper_text":"","type":"imageurl","required":false,"audience_field_name":"Image URL","enabled":false,"order":null,"field_type":"merge","merge_id":6}}).find(function(f) { return f.name === fieldName && f.type === 'smsphone'; }); var isRequired = smsField ? smsField.required : false; var shouldAppendCountryCode = smsNotRequiredRemoveCountryCodeEnabled ? isRequired : true; var phoneInput = document.querySelector('#mce-' + fieldName); if (phoneInput && defaultProgram.countryCallingCode && shouldAppendCountryCode) { phoneInput.value = defaultProgram.countryCallingCode; } displayFlag?.addEventListener('click', function(e) { dropdown.focus(); }); dropdown?.addEventListener('change', function() { const selectedCountry = this.value; if (!selectedCountry || typeof selectedCountry !== 'string') { return; } const flagSpan = displayFlag?.querySelector('#flag-emoji-' + fieldName); if (flagSpan) { flagSpan.textContent = getCountryUnicodeFlag(selectedCountry); flagSpan.setAttribute('aria-label', sanitizeHtml(selectedCountry) + ' flag'); } const selectedProgram = window.MC?.smsPhoneData?.programs.find(function(program) { return program && program.countryCode === selectedCountry; }); var smsNotRequiredRemoveCountryCodeEnabled = true; var smsField = Object.values({"EMAIL":{"name":"EMAIL","label":"Email Address","helper_text":"","type":"email","required":true,"audience_field_name":"Email Address","merge_id":0,"help_text_enabled":false,"enabled":true,"order":0,"field_type":"merge"},"FNAME":{"name":"FNAME","label":"First Name","helper_text":"","type":"text","required":false,"audience_field_name":"First Name","enabled":false,"order":null,"field_type":"merge","merge_id":1},"MMERGE2":{"name":"MMERGE2","label":"Second Name","helper_text":"","type":"text","required":false,"audience_field_name":"Second Name","enabled":false,"order":null,"field_type":"merge","merge_id":2},"MMERGE3":{"name":"MMERGE3","label":"Company Name","helper_text":"","type":"text","required":false,"audience_field_name":"Company Name","enabled":false,"order":null,"field_type":"merge","merge_id":3},"MMERGE4":{"name":"MMERGE4","label":"Job Title","helper_text":"","type":"text","required":false,"audience_field_name":"Job Title","enabled":false,"order":null,"field_type":"merge","merge_id":4},"SIGNUP":{"name":"SIGNUP","label":"Signup form location","helper_text":"","type":"text","required":false,"audience_field_name":"Signup form location","enabled":false,"order":null,"field_type":"merge","merge_id":5},"MMERGE6":{"name":"MMERGE6","label":"Image URL","helper_text":"","type":"imageurl","required":false,"audience_field_name":"Image URL","enabled":false,"order":null,"field_type":"merge","merge_id":6}}).find(function(f) { return f.name === fieldName && f.type === 'smsphone'; }); var isRequired = smsField ? smsField.required : false; var shouldAppendCountryCode = smsNotRequiredRemoveCountryCodeEnabled ? isRequired : true; var phoneInput = document.querySelector('#mce-' + fieldName); if (phoneInput && selectedProgram.countryCallingCode && shouldAppendCountryCode) { phoneInput.value = selectedProgram.countryCallingCode; } updateSmsLegalText(selectedCountry, fieldName); updatePlaceholder(selectedCountry, fieldName); updateCountryCodeInstruction(selectedCountry, fieldName); }); } document.addEventListener('DOMContentLoaded', function() { const smsPhoneFields = document.querySelectorAll('[id^="country-select-"]'); smsPhoneFields.forEach(function(dropdown) { const fieldName = dropdown?.id.replace('country-select-', ''); initializeSmsPhoneDropdown(fieldName); }); });

- Advertisment -

Most Popular

- Advertisment -