UNPKG

ldx-widgets

Version:

widgets

355 lines (287 loc) 11.7 kB
React = require 'react' moment = require 'moment' _ = require 'lodash' {div, a} = React.DOM module.exports = ### * Return Age from datestring * @param {String} datestring * @param {Boolean} return the month default false. ### formatAge: (datestring, includeMonth) -> now = moment().utc() birthDate = moment(datestring).utc() age = '' months = now.diff(birthDate, 'months') if months < 1 days = now.diff(birthDate, 'days') age = "#{days} DO" else if months < 48 age = "#{months} MO" else years = now.diff(birthDate, 'years') age = "#{years} YO" # Include month in age if includeMonth months = months-(years*12) # don't include if 0 months if months then age = "#{years} yrs #{months} mnth" return age formatNHS: (nhs = '') -> # Strip non digit characters out of the nhs nhs = nhs.replace(/[^0-9]/g, "") nhs1 = nhs.substr(0,3) nhs2 = nhs.substr(3,3) nhs3 = nhs.substr(6,4) rv = "#{nhs1}" rv += " #{nhs2}" if nhs2 rv += " #{nhs3}" if nhs3 rv # Returns 10 digit number in (xxx) xxx-xxxx format # or 7 digit number in xxx-xxxx format formatPhoneNumber: (phone) -> if phone? # Make sure the number is a String phone = "#{phone}" phoneClone = phone phoneClone = phoneClone.replace(/[^0-9]/g, '') if phoneClone.length == 10 phoneClone = phoneClone.replace(/(\d{3})(\d{3})(\d{4})/, "($1) $2-$3") return phoneClone if phoneClone.length == 7 phoneClone = phoneClone.replace(/(\d{3})(\d{4})/, "$1-$2") return phoneClone else return phone else return "" formatSource: (options) -> {orgName, facilityName, systemName} = options sourceName = '' # Org if orgName? sourceName += orgName # Facility if facilityName? and facilityName not in [orgName, ''] sourceName += ' - ' if sourceName isnt '' sourceName += facilityName # System if (facilityName? or orgName?) and systemName? and systemName isnt '' sourceName += ' - ' if sourceName isnt '' sourceName += systemName sourceName formatTimezoneForAPI: (relevantDate) -> minutes = moment(relevantDate).utcOffset() if minutes < 0 then negative = "-" else negative = "+" minutes = Math.abs(minutes) hours = Math.floor(minutes / 60) if hours < 10 then hours = "0" + hours minutestring = minutes % 60 if minutestring < 10 then minutestring = "0" + minutestring "#{negative}#{hours}:#{minutestring}" escapeRegExp: (string) -> string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1") isValidEmail : (addr) -> # modified RFC 2822 - http://www.regular-expressions.info/email.html return /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/.test addr # Adding comment to jenkins/github integration. # Converts raw bytes to KB, MB, etc bytesToSize: (bytes, precision) -> unless bytes? then return '' kilobyte = 1024 megabyte = kilobyte * 1024 gigabyte = megabyte * 1024 terabyte = gigabyte * 1024 if bytes >= 0 and bytes < kilobyte then return bytes + ' B' else if bytes >= kilobyte and bytes < megabyte then return (bytes / kilobyte).toFixed(precision) + ' KB' else if bytes >= megabyte and bytes < gigabyte then return (bytes / megabyte).toFixed(precision) + ' MB' else if bytes >= gigabyte and bytes < terabyte then return (bytes / gigabyte).toFixed(precision) + ' GB' else if bytes >= terabyte then return (bytes / terabyte).toFixed(precision) + ' TB' else return bytes + ' B' measureScrollBarWidth: -> $tester = $("<div id='outer' style='overflow: scroll; height: 500px; width: 500px; position: absolute; top: 100px; left: 100px;'><div id='inner' style='position: absolute; height: 100%; width: 100%;'></div><div style='height: 600px; width: 600px;'></div></div>") $('body').append $tester scrollBarWidth = $tester.height() - $tester.find('#inner').height() $tester.remove() return scrollBarWidth # Reduces name to targetLength by replacing a middle portion of the name with an ellipses formatFileName: (name, targetLength) -> # Get the starting file name length fileLength = name.length # Do nothing to files with less than targetLength characters if fileLength <= targetLength then return name # Calculate the number of characters that need to be removed to get the filename down to targetLength # Add 3 to file length as the file length will technically get larger with the ellipses thus we need to increase the file name length # NOTE : it was adding 3 to the targetLength but doing that caused for overlapping characters in the file name removeCount = (fileLength + 3) - targetLength # Figure out how far from the end we should keeps characters # In most cases this will be 6 characters unless the diff between the target length and the removeCount is 6 or else # This will insure the beginning of the file has more than 1 or 2 characters before the ellipses (unless the target length is extremely small like 3) if (targetLength - removeCount) <= 6 then charactersFromEnd = 4 else charactersFromEnd = 6 # Calculate the place the remove should end endRemove = fileLength - charactersFromEnd # Calculate the postion to start the remove startRemove = endRemove - removeCount # NOTE: Negative start values cannot be used in substr becuase IE8 does not support them! # Get the start of the file fileStart = name.substr(0, startRemove) # Get the end of the file fileEnd = name.substr(endRemove) return fileStart + "…" + fileEnd # Gets file extensions from a file name, returns empty string when there is no extensions parseFileExtension: (filename) -> filenameSplit = filename.split('.') return name = if filenameSplit.length > 1 then filenameSplit[filenameSplit.length - 1] else '' # Makes a temparay (somewhat unique) id for use in the GUI before saving a new object to the server makeGuid: -> 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace /[xy]/g, (c) -> r = Math.random()*16|0 v = if c is 'x' then r else (r&0x3|0x8) v.toString(16) # Checks if two object have the same id or guid idsMatch: (itemA, itemB) -> unless itemA? and itemB? then return no if itemA.id? and itemB.id? return itemA.id is itemB.id else if itemA.guid? and itemB.guid? return itemA.guid is itemB.guid else return no # Create a fake mouse event synthesizeMouseEvent: (target, type, options = {}) -> event = target.ownerDocument.createEvent('MouseEvents') opts = type: type canBubble: false # Defaults to false: may need to set to true if we run into a situation where React has issues with events not bubbling to the top cancelable: true view: target.ownerDocument.defaultView detail: 1 screenX: 0 # The coordinates within the entire page screenY: 0 clientX: 0 # The coordinates within the viewport clientY: 0 ctrlKey: false altKey: false shiftKey: false metaKey: false button: 0 # 0 = left, 1 = middle, 2 = right relatedTarget: null # Merge the options with the defaults _.assign(opts, options) # Pass in the options event.initMouseEvent( opts.type opts.canBubble opts.cancelable opts.view opts.detail opts.screenX opts.screenY opts.clientX opts.clientY opts.ctrlKey opts.altKey opts.shiftKey opts.metaKey opts.button opts.relatedTarget ) # Fire the event target.dispatchEvent(event) # Checks to see if input is a number isNumber: (input) -> return !isNaN(parseFloat(input)) and isFinite(input) # Limits a value to only two decimal places toFixed: (value) -> return Math.round(value * 100) / 100 # Can measure widths and heights of DOM elements measureDOMProp: (el, DOMProp) -> measurer = document.getElementById 'measurer' measurer.appendChild el prop = el[DOMProp] measurer.removeChild el prop # Make sure URL begins with 'https' checkHTTPS: (url) -> checkString = url.slice 0, 5 return checkString is "https" buildFormData: (context = @, newData = {}) -> refs = context['refs'] unless refs? then throw Error("Form Data Builder: No 'refs' object found on context #{context}") #################### # Define the iterate function that will run for each key part #################### iterate = (data, keyParts, getValue) => # Nested properties cur = keyParts[0] next = data matchArray = cur.match(/\[(\d+)\]/) matchCollection = cur.match(/\[\{(.+)\}\]/) nextIsArray = keyParts[1].match(/\[(.+)\]/) if keyParts[1]? arrayKey = matchArray[1] if matchArray? collectionKey = matchCollection[1].split(':') if matchCollection? # Check the object to establish the default type # Arrays if arrayKey unless data? then data = [] next = data[arrayKey] cur = arrayKey # Collections else if collectionKey unless data? then data = [] collectionRecord = null k = collectionKey[0] v = collectionKey[1] # Check if the value contains a state variable valRef = v.match(/\$(\w+)/) if valRef? val = context['state'] # Make sure state is defined unless val? then throw Error("Form Data Builder: unable to pull state properties when local state is undefined") # Double-nested refs subKeys = v.replace(/^\$/, '').split('.') while subKeys.length val = val[subKeys.shift()] v = val for obj, i in data when obj? # Cast the prop value to a string, in case of a number/boolean type if obj[k]? and String(obj[k]) is String(v) collectionRecord = next = data[i] break unless collectionRecord? # If no record is found, push a new record in with the matching property d = {} d[k] = v data.push(d) next = data[data.length - 1] # Object else if not data[cur]? next = data[cur] = if nextIsArray then [] else {} # If it already exists else next = data[cur] # Base case. Return the value of the field once the destination prop has been reached unless keyParts.length > 1 data[cur] = getValue() return data # Remove the first item to continue recursion keyParts.shift() return iterate(next, keyParts, getValue) #################### # Loop through the refs, running iterate for each key part # Refs can be skipped by prepending with '!' #################### for key, ref of refs when key[0] isnt '!' and ref.getValue? # Check for nested properties if key.match(/\.(?![^[\]]*])/) keyParts = key.split(/\.(?![^[\]]*])/g) getValue = ref.getValue # Don't move on unless getValue exists unless getValue? then throw Error("Ref #{key} does not have a getValue method defined") # Iterate over each key iterate(newData, keyParts, getValue) # Set properties directly when not using nesting else newData[key] = ref.getValue() return newData