(function (angular) { angular.module("ui.materialize", ["ui.materialize.ngModel", "ui.materialize.collapsible", "ui.materialize.toast", "ui.materialize.sidenav", "ui.materialize.material_select", "ui.materialize.dropdown", "ui.materialize.inputfield", "ui.materialize.input_date", "ui.materialize.tabs", "ui.materialize.pagination", "ui.materialize.pushpin", "ui.materialize.scrollspy", "ui.materialize.parallax","ui.materialize.modal", "ui.materialize.tooltipped", "ui.materialize.slider", "ui.materialize.materialboxed"]); angular.module("ui.materialize.ngModel", []) .directive("ngModel",["$timeout", function($timeout){ return { restrict: 'A', priority: -1, // lower priority than built-in ng-model so it runs first link: function(scope, element, attr) { scope.$watch(attr.ngModel,function(value){ $timeout(function () { if (value){ element.trigger("change"); } else if(element.attr('placeholder') === undefined) { if(!element.is(":focus")) element.trigger("blur"); } }); }); } }; }]); /* example usage:
*/ angular.module("ui.materialize.slider", []) .directive("slider", ["$timeout", function($timeout){ return { restrict: 'A', scope: { height: '=', transition: '=', interval: '=', indicators: '=' }, link: function(scope, element, attrs) { element.addClass("slider"); $timeout(function(){ element.slider({ height: (angular.isDefined(scope.height)) ? scope.height : 400, transition: (angular.isDefined(scope.transition)) ? scope.transition : 500, interval: (angular.isDefined(scope.interval)) ? scope.interval : 6000, indicators: (angular.isDefined(scope.indicators)) ? scope.indicators : true }); }); } }; }]); angular.module("ui.materialize.collapsible", []) .directive("collapsible", ["$timeout", function ($timeout) { return { link: function (scope, element, attrs) { $timeout(function () { element.collapsible(); }); if ("watch" in attrs) { scope.$watch(function () { return element[0].innerHTML; }, function (oldVal, newVal) { if (oldVal !== newVal) { $timeout(function () { element.collapsible(); }); } }); } } }; }]); angular.module("ui.materialize.parallax", []) .directive("parallax", ["$timeout", function($timeout){ return { link: function(scope, element, attrs) { $timeout(function(){ element.parallax(); }); } }; }]); angular.module("ui.materialize.toast", []) .constant("toastConfig", { duration: 3000, rounded: "rounded" }) .directive("toast", ["toastConfig", function (toastConfig) { return { scope: { message: "@", duration: "@" }, link: function (scope, element, attrs) { element.bind(attrs.toast, function () { var message = (angular.isDefined(scope.message)) ? scope.message : ""; var rounded = (angular.isDefined(attrs.rounded)) ? toastConfig.rounded : null; Materialize.toast(message, scope.duration ? scope.duration : toastConfig.duration, rounded); }); } }; }]); angular.module('ui.materialize.pushpin', []) .directive('pushpin', [function(){ return { restrict: 'AE', require: [ '?pushpinTop', '?pushpinOffset', '?pushpinBottom' ], link: function (scope, element, attrs) { var top = attrs.pushpinTop || 0; var offset = attrs.pushpinOffset || 0; var bottom = attrs.pushpinBottom || Infinity; setTimeout(function () { element.pushpin({top: top, offset: offset, bottom: bottom}); }, 0); } }; }]); // TODO: Add some documentation for this. angular.module("ui.materialize.scrollspy", []) .directive("scrollspy", ["$timeout", function($timeout){ return { restrict: 'A', link: function(scope, element, attrs) { element.addClass("scrollspy"); $timeout(function(){ element.scrollSpy(); }); } }; }]); angular.module("ui.materialize.tabs", []) .directive("tabs", [function(){ return { link: function (scope, element, attrs) { element.tabs(); } }; }]); // Example: // data-activates is handled by the jQuery plugin. angular.module("ui.materialize.sidenav", []) .directive("sidenav", [function () { return { scope: { menuwidth: "@", closeonclick: "@" }, link: function (scope, element, attrs) { element.sideNav({ menuWidth: (angular.isDefined(scope.menuwidth)) ? scope.menuwidth : undefined, edge: attrs.sidenav ? attrs.sidenav : "left", closeOnClick: (angular.isDefined(scope.closeonclick)) ? scope.closeonclick == "true" : undefined }); } }; }]); // This works, unless the content inside the select changes. angular.module("ui.materialize.material_select", []) .directive("materialSelect", ["$compile", "$timeout", function ($compile, $timeout) { return { link: function (scope, element, attrs) { if (element.is("select")) { $compile(element.contents())(scope); function initSelect() { element.siblings(".caret").remove(); element.material_select(); } $timeout(initSelect); if (attrs.ngModel) { scope.$watch(attrs.ngModel, initSelect); } if ("watch" in attrs) { scope.$watch(function () { return element[0].innerHTML; }, function (oldVal, newVal) { if (oldVal !== newVal) { $timeout(initSelect); } }); } } } }; }]); /* Example usage, notice the empty dropdown tag in the dropdown trigger. Select a demo */ angular.module("ui.materialize.dropdown", []) .directive("dropdown", ["$compile", "$timeout", function ($compile, $timeout) { return { scope: { inDuration: "@", outDuration: "@", constrainWidth: "@", hover: "@", alignment: "@", gutter: "@", belowOrigin: "@" }, link: function (scope, element, attrs) { $timeout(function () { $compile(element.contents())(scope); element.dropdown({ inDuration: (angular.isDefined(scope.inDuration)) ? scope.inDuration : undefined, outDuration: (angular.isDefined(scope.outDuration)) ? scope.outDuration : undefined, constrain_width: (angular.isDefined(scope.constrainWidth)) ? scope.constrainWidth : undefined, hover: (angular.isDefined(scope.hover)) ? scope.hover : undefined, alignment: (angular.isDefined(scope.alignment)) ? scope.alignment : undefined, gutter: (angular.isDefined(scope.gutter)) ? scope.gutter : undefined, belowOrigin: (angular.isDefined(scope.belowOrigin)) ? scope.belowOrigin : undefined }); }); } }; }]); /** * Instead of adding the .input-field class to a div surrounding a label and input, add the attribute input-field. * That way it will also work when angular destroys/recreates the elements. * * Example: */ angular.module("ui.materialize.inputfield", []) .directive('inputField', ["$compile", "$timeout", function ($compile, $timeout) { return { transclude: true, scope: {}, link: function (scope, element) { $timeout(function () { Materialize.updateTextFields(); // The "> > [selector]", is to restrict to only those tags that are direct children of the directive element. Otherwise we might hit to many elements with the selectors. // Triggering autoresize of the textareas. element.find("> > .materialize-textarea").each(function () { var that = $(this); that.addClass("materialize-textarea"); that.trigger("autoresize"); var model = that.attr("ng-model"); if (model) { scope.$parent.$watch(model, function (a, b) { if (a !== b) { $timeout(function () { that.trigger("autoresize"); }); } }); } }); // Adding char-counters. element.find('> > .materialize-textarea, > > input').each(function (index, countable) { countable = angular.element(countable); if (!countable.siblings('span[class="character-counter"]').length) { countable.characterCounter(); } }); }); }, template: '
' }; }]); /** * Add pickadate directive * Type text is mandatory * Example: */ angular.module("ui.materialize.input_date", []) .directive('inputDate', ["$compile", "$timeout", function ($compile, $timeout) { // Fix for issue 46. This gotta be a bug in the materialize code, but this fixes it. var style = $(''); $('html > head').append(style); // Define Prototype Date format // Use like this // today = new Date(); // var dateString = today.format("dd-m-yy"); var dateFormat = function () { var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g, timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, timezoneClip = /[^-+\dA-Z]/g, pad = function (val, len) { val = String(val); len = len || 2; while (val.length < len) { val = "0" + val; } return val; }; // Regexes and supporting functions are cached through closure return function (date, mask, utc) { var dF = dateFormat; // You can't provide utc if you skip other args (use the "UTC:" mask prefix) if (arguments.length === 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) { mask = date; date = undefined; } // Passing date through Date applies Date.parse, if necessary date = date ? new Date(date) : new Date(); if (isNaN(date)) throw SyntaxError("invalid date"); mask = String(dF.masks[mask] || mask || dF.masks["default"]); // Allow setting the utc argument via the mask if (mask.slice(0, 4) == "UTC:") { mask = mask.slice(4); utc = true; } var _ = utc ? "getUTC" : "get", d = date[ _ + "Date" ](), D = date[ _ + "Day" ](), m = date[ _ + "Month" ](), y = date[ _ + "FullYear" ](), H = date[ _ + "Hours" ](), M = date[ _ + "Minutes" ](), s = date[ _ + "Seconds" ](), L = date[ _ + "Milliseconds" ](), o = utc ? 0 : date.getTimezoneOffset(), flags = { d: d, dd: pad(d), ddd: dF.i18n.dayNames[D], dddd: dF.i18n.dayNames[D + 7], m: m + 1, mm: pad(m + 1), mmm: dF.i18n.monthNames[m], mmmm: dF.i18n.monthNames[m + 12], yy: String(y).slice(2), yyyy: y, h: H % 12 || 12, hh: pad(H % 12 || 12), H: H, HH: pad(H), M: M, MM: pad(M), s: s, ss: pad(s), l: pad(L, 3), L: pad(L > 99 ? Math.round(L / 10) : L), t: H < 12 ? "a" : "p", tt: H < 12 ? "am" : "pm", T: H < 12 ? "A" : "P", TT: H < 12 ? "AM" : "PM", Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""), o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4), S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10] }; return mask.replace(token, function ($0) { return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1); }); }; }(); // Some common format strings dateFormat.masks = { "default": "ddd mmm dd yyyy HH:MM:ss", shortDate: "m/d/yy", mediumDate: "mmm d, yyyy", longDate: "mmmm d, yyyy", fullDate: "dddd, mmmm d, yyyy", shortTime: "h:MM TT", mediumTime: "h:MM:ss TT", longTime: "h:MM:ss TT Z", isoDate: "yyyy-mm-dd", isoTime: "HH:MM:ss", isoDateTime: "yyyy-mm-dd'T'HH:MM:ss", isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'" }; // Internationalization strings dateFormat.i18n = { dayNames: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], monthNames: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] }; // For convenience... Date.prototype.format = function (mask, utc) { return dateFormat(this, mask, utc); }; /** * Validate date object * @param {Date} date * @return {Boolean} */ var isValidDate = function(date) { if( Object.prototype.toString.call(date) === '[object Date]' ) { return !isNaN(date.getTime()); } return false; }; return { require: 'ngModel', scope: { container: "@", format: "@", formatSubmit: "@", monthsFull: "@", monthsShort: "@", weekdaysFull: "@", weekdaysLetter: "@", disable: "=", today: "=", clear: "=", close: "=", selectYears: "=", onStart: "&", onRender: "&", onOpen: "&", onClose: "&", onSet: "&", onStop: "&", ngReadonly: "=?", max: "@", min: "@" }, link: function (scope, element, attrs, ngModelCtrl) { ngModelCtrl.$formatters.unshift(function (modelValue) { if (modelValue) { var date = new Date(modelValue); return (angular.isDefined(scope.format)) ? date.format(scope.format) : date.format('d mmmm, yyyy'); } return null; }); var monthsFull = (angular.isDefined(scope.monthsFull)) ? scope.$eval(scope.monthsFull) : undefined, monthsShort = (angular.isDefined(scope.monthsShort)) ? scope.$eval(scope.monthsShort) : undefined, weekdaysFull = (angular.isDefined(scope.weekdaysFull)) ? scope.$eval(scope.weekdaysFull) : undefined, weekdaysLetter = (angular.isDefined(scope.weekdaysLetter)) ? scope.$eval(scope.weekdaysLetter) : undefined; $compile(element.contents())(scope); if (!(scope.ngReadonly)) { $timeout(function () { var pickadateInput = element.pickadate({ container : (angular.isDefined(scope.container)) ? scope.container : 'body', format: (angular.isDefined(scope.format)) ? scope.format : undefined, formatSubmit: (angular.isDefined(scope.formatSubmit)) ? scope.formatSubmit : undefined, monthsFull: (angular.isDefined(monthsFull)) ? monthsFull : undefined, monthsShort: (angular.isDefined(monthsShort)) ? monthsShort : undefined, weekdaysFull: (angular.isDefined(weekdaysFull)) ? weekdaysFull : undefined, weekdaysLetter: (angular.isDefined(weekdaysLetter)) ? weekdaysLetter : undefined, disable: (angular.isDefined(scope.disable)) ? scope.disable : undefined, today: (angular.isDefined(scope.today)) ? scope.today : undefined, clear: (angular.isDefined(scope.clear)) ? scope.clear : undefined, close: (angular.isDefined(scope.close)) ? scope.close : undefined, selectYears: (angular.isDefined(scope.selectYears)) ? scope.selectYears : undefined, onStart: (angular.isDefined(scope.onStart)) ? function(){ scope.onStart(); } : undefined, onRender: (angular.isDefined(scope.onRender)) ? function(){ scope.onRender(); } : undefined, onOpen: (angular.isDefined(scope.onOpen)) ? function(){ scope.onOpen(); } : undefined, onClose: (angular.isDefined(scope.onClose)) ? function(){ scope.onClose(); } : undefined, onSet: (angular.isDefined(scope.onSet)) ? function(){ scope.onSet(); } : undefined, onStop: (angular.isDefined(scope.onStop)) ? function(){ scope.onStop(); } : undefined }); //pickadate API var picker = pickadateInput.pickadate('picker'); //watcher of min and max scope.$watch('max', function(newMax) { if( picker ) { var maxDate = new Date(newMax); picker.set({max: isValidDate(maxDate) ? maxDate : false}); } }); scope.$watch('min', function(newMin) { if( picker ) { var minDate = new Date(newMin); picker.set({min: isValidDate(minDate) ? minDate : false}); } }); }); } } }; }]); /** * Example: * ul-class could be either an object or a string * * Based on https://github.com/brantwills/Angular-Paging */ angular.module("ui.materialize.pagination", []) .directive('pagination', function () { // Assign null-able scope values from settings function setScopeValues(scope, attrs) { scope.List = []; scope.Hide = false; scope.page = parseInt(scope.page) || 1; scope.total = parseInt(scope.total) || 0; scope.dots = scope.dots || '...'; scope.ulClass = scope.ulClass || attrs.ulClass || 'pagination'; scope.adjacent = parseInt(scope.adjacent) || 2; scope.activeClass = 'active'; scope.disabledClass = 'disabled'; scope.scrollTop = scope.$eval(attrs.scrollTop); scope.hideIfEmpty = scope.$eval(attrs.hideIfEmpty); scope.showPrevNext = scope.$eval(attrs.showPrevNext); } // Validate and clean up any scope values // This happens after we have set the // scope values function validateScopeValues(scope, pageCount) { // Block where the page is larger than the pageCount if (scope.page > pageCount) { scope.page = pageCount; } // Block where the page is less than 0 if (scope.page <= 0) { scope.page = 1; } // Block where adjacent value is 0 or below if (scope.adjacent <= 0) { scope.adjacent = 2; } // Hide from page if we have 1 or less pages // if directed to hide empty if (pageCount <= 1) { scope.Hide = scope.hideIfEmpty; } } // Internal Pagination Click Action function internalAction(scope, page) { // Block clicks we try to load the active page if (scope.page == page) { return; } // Update the page in scope and fire any paging actions scope.page = page; scope.paginationAction({ page: page }); // If allowed scroll up to the top of the page if (scope.scrollTop) { scrollTo(0, 0); } } // Add Range of Numbers function addRange(start, finish, scope) { var i = 0; for (i = start; i <= finish; i++) { var item = { value: i.toString(), liClass: scope.page == i ? scope.activeClass : 'waves-effect', action: function() { internalAction(scope, this.value); } }; scope.List.push(item); } } // Add Dots ie: 1 2 [...] 10 11 12 [...] 56 57 function addDots(scope) { scope.List.push({ value: scope.dots }); } // Add First Pages function addFirst(scope, next) { addRange(1, 2, scope); // We ignore dots if the next value is 3 // ie: 1 2 [...] 3 4 5 becomes just 1 2 3 4 5 if (next != 3) { addDots(scope); } } /** * Add the first, previous, next, and last buttons if desired * The logic is defined by the mode of interest * This method will simply return if the scope.showPrevNext is false * This method will simply return if there are no pages to display * * @param {Object} scope - The local directive scope object * @param {int} pageCount - The last page number or total page count * @param {string} mode - The mode of interest either prev or last */ function addPrevNext(scope, pageCount, mode){ // Ignore if we are not showing // or there are no pages to display if (!scope.showPrevNext || pageCount < 1) { return; } // Local variables to help determine logic var disabled, alpha, beta; // Determine logic based on the mode of interest // Calculate the previous / next page and if the click actions are allowed if(mode === 'prev') { disabled = scope.page - 1 <= 0; var prevPage = scope.page - 1 <= 0 ? 1 : scope.page - 1; alpha = { value : "<<", title: 'First Page', page: 1 }; beta = { value: "<", title: 'Previous Page', page: prevPage }; } else { disabled = scope.page + 1 > pageCount; var nextPage = scope.page + 1 >= pageCount ? pageCount : scope.page + 1; alpha = { value : ">", title: 'Next Page', page: nextPage }; beta = { value: ">>", title: 'Last Page', page: pageCount }; } // Create the Add Item Function var addItem = function(item, disabled){ scope.List.push({ value: item.value, title: item.title, liClass: disabled ? scope.disabledClass : '', action: function(){ if(!disabled) { internalAction(scope, item.page); } } }); }; // Add our items addItem(alpha, disabled); addItem(beta, disabled); } function addLast(pageCount, scope, prev) { // We ignore dots if the previous value is one less that our start range // ie: 1 2 3 4 [...] 5 6 becomes just 1 2 3 4 5 6 if (prev != pageCount -2) { addDots(scope); } addRange(pageCount -1, pageCount, scope); } // Main build function function build(scope, attrs) { // Block divide by 0 and empty page size if (!scope.pageSize || scope.pageSize < 0) { return; } // Assign scope values setScopeValues(scope, attrs); // local variables var start, size = scope.adjacent * 2, pageCount = Math.ceil(scope.total / scope.pageSize); // Validation Scope validateScopeValues(scope, pageCount); // Add the Next and Previous buttons to our list addPrevNext(scope, pageCount, 'prev'); if (pageCount < (5 + size)) { start = 1; addRange(start, pageCount, scope); } else { var finish; if (scope.page <= (1 + size)) { start = 1; finish = 2 + size + (scope.adjacent - 1); addRange(start, finish, scope); addLast(pageCount, scope, finish); } else if (pageCount - size > scope.page && scope.page > size) { start = scope.page - scope.adjacent; finish = scope.page + scope.adjacent; addFirst(scope, start); addRange(start, finish, scope); addLast(pageCount, scope, finish); } else { start = pageCount - (1 + size + (scope.adjacent - 1)); finish = pageCount; addFirst(scope, start); addRange(start, finish, scope); } } addPrevNext(scope, pageCount, 'next'); } return { restrict: 'EA', scope: { page: '=', pageSize: '=', total: '=', dots: '@', hideIfEmpty: '@', adjacent: '@', scrollTop: '@', showPrevNext: '@', paginationAction: '&', ulClass: '=?' }, template: '', link: function (scope, element, attrs) { // Hook in our watched items scope.$watchCollection('[page, total, pageSize]', function () { build(scope, attrs); }); } }; }); /* example usage: show Modal */ angular.module("ui.materialize.modal", []) .directive("modal", ["$compile", "$timeout", function ($compile, $timeout) { return { scope: { dismissible: "=", opacity: "@", inDuration: "@", outDuration: "@" }, link: function (scope, element, attrs) { $compile(element.contents())(scope); $timeout(function () { element.leanModal({ dismissible: (angular.isDefined(scope.dismissible)) ? scope.dismissible : undefined, opacity: (angular.isDefined(scope.opacity)) ? scope.opacity : undefined, in_duration: (angular.isDefined(scope.inDuration)) ? scope.inDuration : undefined, out_duration: (angular.isDefined(scope.outDuration)) ? scope.outDuration : undefined }); }); } }; }]); /* example usage: Hover me! */ angular.module("ui.materialize.tooltipped", []) .directive("tooltipped", ["$compile", "$timeout", function ($compile, $timeout) { return { restrict: "EA", scope: true, link: function (scope, element, attrs) { element.addClass("tooltipped"); $compile(element.contents())(scope); $timeout(function () { element.tooltip(); }); scope.$on('$destroy', function () { element.tooltip("remove"); }); } }; }]); /* example usage: */ angular.module("ui.materialize.materialboxed", []) .directive("materialboxed", ["$timeout", function($timeout){ return { restrict: 'A', link: function(scope, element, attrs) { $timeout(function(){ element.materialbox(); }); } }; }]); }(angular));