From 791b53958200a72eb1713dd59bdf9bcbcc685a20 Mon Sep 17 00:00:00 2001 From: trigras Date: Tue, 16 Jun 2015 00:29:43 +0300 Subject: [PATCH 01/62] lt language files updated --- resources/lang/lt/pagination.php | 31 +++-- resources/lang/lt/reminders.php | 37 +++--- resources/lang/lt/validation.php | 195 ++++++++++++++++--------------- 3 files changed, 132 insertions(+), 131 deletions(-) diff --git a/resources/lang/lt/pagination.php b/resources/lang/lt/pagination.php index eb9be3baaed5..d2ba5375c39c 100644 --- a/resources/lang/lt/pagination.php +++ b/resources/lang/lt/pagination.php @@ -1,20 +1,19 @@ - '« Previous', + 'previous' => '« Ankstesnis', + 'next' => 'Sekantis »', - 'next' => 'Next »', - -); \ No newline at end of file +]; diff --git a/resources/lang/lt/reminders.php b/resources/lang/lt/reminders.php index ad2262124d4d..05af271289f3 100644 --- a/resources/lang/lt/reminders.php +++ b/resources/lang/lt/reminders.php @@ -1,24 +1,21 @@ "Passwords must be at least six characters and match the confirmation.", - - "user" => "We can't find a user with that e-mail address.", - - "token" => "This password reset token is invalid.", - - "sent" => "Password reminder sent!", - -); \ No newline at end of file + "password" => "Slaptažodis turi būti bent šešių simbolių ir sutapti su patvirtinimu.", + "user" => "Vartotojas su tokiu el. pašu nerastas.", + "token" => "Šis slaptažodžio raktas yra neteisingas.", + "sent" => "Naujo slaptažodžio nustatymo nuoroda išsiųsta", + "reset" => "Nustatytas naujas slaptažodis!", +]; diff --git a/resources/lang/lt/validation.php b/resources/lang/lt/validation.php index 1c12d36aa4ca..7a76d1c22a8b 100644 --- a/resources/lang/lt/validation.php +++ b/resources/lang/lt/validation.php @@ -1,103 +1,108 @@ "The :attribute must be accepted.", - "active_url" => "The :attribute is not a valid URL.", - "after" => "The :attribute must be a date after :date.", - "alpha" => "The :attribute may only contain letters.", - "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", - "alpha_num" => "The :attribute may only contain letters and numbers.", - "array" => "The :attribute must be an array.", - "before" => "The :attribute must be a date before :date.", - "between" => array( - "numeric" => "The :attribute must be between :min - :max.", - "file" => "The :attribute must be between :min - :max kilobytes.", - "string" => "The :attribute must be between :min - :max characters.", - "array" => "The :attribute must have between :min - :max items.", - ), - "confirmed" => "The :attribute confirmation does not match.", - "date" => "The :attribute is not a valid date.", - "date_format" => "The :attribute does not match the format :format.", - "different" => "The :attribute and :other must be different.", - "digits" => "The :attribute must be :digits digits.", - "digits_between" => "The :attribute must be between :min and :max digits.", - "email" => "The :attribute format is invalid.", - "exists" => "The selected :attribute is invalid.", - "image" => "The :attribute must be an image.", - "in" => "The selected :attribute is invalid.", - "integer" => "The :attribute must be an integer.", - "ip" => "The :attribute must be a valid IP address.", - "max" => array( - "numeric" => "The :attribute may not be greater than :max.", - "file" => "The :attribute may not be greater than :max kilobytes.", - "string" => "The :attribute may not be greater than :max characters.", - "array" => "The :attribute may not have more than :max items.", - ), - "mimes" => "The :attribute must be a file of type: :values.", - "min" => array( - "numeric" => "The :attribute must be at least :min.", - "file" => "The :attribute must be at least :min kilobytes.", - "string" => "The :attribute must be at least :min characters.", - "array" => "The :attribute must have at least :min items.", - ), - "not_in" => "The selected :attribute is invalid.", - "numeric" => "The :attribute must be a number.", - "regex" => "The :attribute format is invalid.", - "required" => "The :attribute field is required.", - "required_if" => "The :attribute field is required when :other is :value.", - "required_with" => "The :attribute field is required when :values is present.", - "required_without" => "The :attribute field is required when :values is not present.", - "same" => "The :attribute and :other must match.", - "size" => array( - "numeric" => "The :attribute must be :size.", - "file" => "The :attribute must be :size kilobytes.", - "string" => "The :attribute must be :size characters.", - "array" => "The :attribute must contain :size items.", - ), - "unique" => "The :attribute has already been taken.", - "url" => "The :attribute format is invalid.", - - "positive" => "The :attribute must be greater than zero.", - "has_credit" => "The client does not have enough credit.", - "notmasked" => "The values are masked", - "less_than" => 'The :attribute must be less than :value', - - /* - |-------------------------------------------------------------------------- - | Custom Validation Language Lines - |-------------------------------------------------------------------------- - | - | Here you may specify custom validation messages for attributes using the - | convention "attribute.rule" to name the lines. This makes it quick to - | specify a specific custom language line for a given attribute rule. - | - */ + "accepted" => "Laukas :attribute turi būti priimtas.", + "active_url" => "Laukas :attribute nėra galiojantis internetinis adresas.", + "after" => "Laukelyje :attribute turi būti data po :date.", + "alpha" => "Laukas :attribute gali turėti tik raides.", + "alpha_dash" => "Laukas :attribute gali turėti tik raides, skaičius ir brūkšnelius.", + "alpha_num" => "Laukas :attribute gali turėti tik raides ir skaičius.", + "array" => "Laukas :attribute turi būti masyvas.", + "before" => "Laukas :attribute turi būti data prieš :date.", + "between" => [ + "numeric" => "Lauko :attribute reikšmė turi būti tarp :min ir :max.", + "file" => "Failo dydis lauke :attribute turi būti tarp :min ir :max kilobaitų.", + "string" => "Simbolių skaičius lauke :attribute turi būti tarp :min ir :max.", + "array" => "Elementų skaičius lauke :attribute turi turėti nuo :min iki :max.", + ], + "boolean" => "Lauko reikšmė :attribute turi būti 'taip' arba 'ne'.", + "confirmed" => "Lauko :attribute patvirtinimas nesutampa.", + "date" => "Lauko :attribute reikšmė nėra galiojanti data.", + "date_format" => "Lauko :attribute reikšmė neatitinka formato :format.", + "different" => "Laukų :attribute ir :other reikšmės turi skirtis.", + "digits" => "Laukas :attribute turi būti sudarytas iš :digits skaitmenų.", + "digits_between" => "Laukas :attribute tuti turėti nuo :min iki :max skaitmenų.", + "email" => "Lauko :attribute reikšmė turi būti galiojantis el. pašto adresas.", + "filled" => "Laukas :attribute turi būti užpildytas.", + "exists" => "Pasirinkta negaliojanti :attribute reikšmė.", + "image" => "Lauko :attribute reikšmė turi būti paveikslėlis.", + "in" => "Pasirinkta negaliojanti :attribute reikšmė.", + "integer" => "Lauko :attribute reikšmė turi būti veikasis skaičius.", + "ip" => "Lauko :attribute reikšmė turi būti galiojantis IP adresas.", + "max" => [ + "numeric" => "Lauko :attribute reikšmė negali būti didesnė nei :max.", + "file" => "Failo dydis lauke :attribute reikšmė negali būti didesnė nei :max kilobaitų.", + "string" => "Simbolių kiekis lauke :attribute reikšmė negali būti didesnė nei :max simbolių.", + "array" => "Elementų kiekis lauke :attribute negali turėti daugiau nei :max elementų.", + ], + "mimes" => "Lauko reikšmė :attribute turi būti failas vieno iš sekančių tipų: :values.", + "min" => [ + "numeric" => "Lauko :attribute reikšmė turi būti ne mažesnė nei :min.", + "file" => "Failo dydis lauke :attribute turi būti ne mažesnis nei :min kilobaitų.", + "string" => "Simbolių kiekis lauke :attribute turi būti ne mažiau nei :min.", + "array" => "Elementų kiekis lauke :attribute turi būti ne mažiau nei :min.", + ], + "not_in" => "Pasirinkta negaliojanti reikšmė :attribute.", + "numeric" => "Lauko :attribute reikšmė turi būti skaičius.", + "regex" => "Negaliojantis lauko :attribute formatas.", + "required" => "Privaloma užpildyti lauką :attribute.", + "required_if" => "Privaloma užpildyti lauką :attribute kai :other yra :value.", + "required_with" => "Privaloma užpildyti lauką :attribute kai pateikta :values.", + "required_with_all" => "Privaloma užpildyti lauką :attribute kai pateikta :values.", + "required_without" => "Privaloma užpildyti lauką :attribute kai nepateikta :values.", + "required_without_all" => "Privaloma užpildyti lauką :attribute kai nepateikta nei viena iš reikšmių :values.", + "same" => "Laukai :attribute ir :other turi sutapti.", + "size" => [ + "numeric" => "Lauko :attribute reikšmė turi būti :size.", + "file" => "Failo dydis lauke :attribute turi būti :size kilobaitai.", + "string" => "Simbolių skaičius lauke :attribute turi būti :size.", + "array" => "Elementų kiekis lauke :attribute turi būti :size.", + ], + "string" => "The :attribute must be a string.", + "timezone" => "Lauko :attribute reikšmė turi būti galiojanti laiko zona.", + "unique" => "Tokia :attribute reikšmė jau pasirinkta.", + "url" => "Negaliojantis lauko :attribute formatas.", - 'custom' => array(), + /* + |-------------------------------------------------------------------------- + | Pasirinktiniai patvirtinimo kalbos eilutės + |-------------------------------------------------------------------------- + | + | Čia galite nurodyti pasirinktinius patvirtinimo pranešimus, naudodami + | konvenciją "attribute.rule" eilučių pavadinimams. Tai leidžia greitai + | nurodyti konkrečią pasirinktinę kalbos eilutę tam tikrai atributo taisyklei. + | + */ - /* - |-------------------------------------------------------------------------- - | Custom Validation Attributes - |-------------------------------------------------------------------------- - | - | The following language lines are used to swap attribute place-holders - | with something more reader friendly such as E-Mail Address instead - | of "email". This simply helps us make messages a little cleaner. - | - */ + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], - 'attributes' => array(), + /* + |-------------------------------------------------------------------------- + | Pasirinktiniai patvirtinimo atributai + |-------------------------------------------------------------------------- + | + | Sekančios kalbos eilutės naudojamos pakeisti vietos žymes + | kuo nors labiau priimtinu skaitytojui (pvz. "El.Pašto Adresas" vietoj + | "email". TTai tiesiog padeda mums padaryti žinutes truputi aiškesnėmis. + | + */ -); + 'attributes' => [], + +]; From d558ba24c32c85e70f7b766c58cdc343732ac264 Mon Sep 17 00:00:00 2001 From: trigras Date: Tue, 16 Jun 2015 01:15:09 +0300 Subject: [PATCH 02/62] html lang --- resources/views/emails/auth/reminder.blade.php | 2 +- resources/views/emails/invoice_viewed_html.blade.php | 2 +- resources/views/master.blade.php | 2 +- resources/views/setup.blade.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/views/emails/auth/reminder.blade.php b/resources/views/emails/auth/reminder.blade.php index 2976327b5df2..bb68353b70f8 100644 --- a/resources/views/emails/auth/reminder.blade.php +++ b/resources/views/emails/auth/reminder.blade.php @@ -1,5 +1,5 @@ - + diff --git a/resources/views/emails/invoice_viewed_html.blade.php b/resources/views/emails/invoice_viewed_html.blade.php index 3a0aaa975b7b..957b6bff8efa 100644 --- a/resources/views/emails/invoice_viewed_html.blade.php +++ b/resources/views/emails/invoice_viewed_html.blade.php @@ -1,5 +1,5 @@ - + diff --git a/resources/views/master.blade.php b/resources/views/master.blade.php index 9d6d07c489f1..d0b5630815f6 100644 --- a/resources/views/master.blade.php +++ b/resources/views/master.blade.php @@ -1,5 +1,5 @@ - + Invoice Ninja | {{ isset($title) ? $title : ' ' . trans('texts.app_title') }} diff --git a/resources/views/setup.blade.php b/resources/views/setup.blade.php index e65ebae5d7f7..6aada81e4afe 100644 --- a/resources/views/setup.blade.php +++ b/resources/views/setup.blade.php @@ -1,5 +1,5 @@ - + Invoice Ninja | Setup From dd06629bfc2ed4f8be341400194e4c6722903890 Mon Sep 17 00:00:00 2001 From: trigras Date: Tue, 16 Jun 2015 01:19:05 +0300 Subject: [PATCH 03/62] latin-ext font encoding added --- resources/views/master.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/master.blade.php b/resources/views/master.blade.php index d0b5630815f6..e70592d63e29 100644 --- a/resources/views/master.blade.php +++ b/resources/views/master.blade.php @@ -18,8 +18,8 @@ - - + + From 23d9a2728bd6ae6e6c1d2c1710e9fcc27a2c201d Mon Sep 17 00:00:00 2001 From: Patrick Samson Date: Thu, 18 Jun 2015 19:28:43 -0400 Subject: [PATCH 04/62] Fix Charts & Reports queries using SQLite --- app/Http/Controllers/ReportController.php | 78 +++++++++++++++++------ 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index b1746761b10f..50a529a701ba 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -1,6 +1,7 @@ balance; } - if ($action == 'export') { + if ($action == 'export') + { self::export($exportData, $reportTotals); } } - if ($enableChart) { - foreach ([ENTITY_INVOICE, ENTITY_PAYMENT, ENTITY_CREDIT] as $entityType) { - $records = DB::table($entityType.'s') - ->select(DB::raw('sum(amount) as total, concat(YEAR('.$entityType.'_date), '.$groupBy.'('.$entityType.'_date)) as '.$groupBy)) - ->where('account_id', '=', Auth::user()->account_id) - ->where($entityType.'s.is_deleted', '=', false) - ->where($entityType.'s.'.$entityType.'_date', '>=', $startDate->format('Y-m-d')) - ->where($entityType.'s.'.$entityType.'_date', '<=', $endDate->format('Y-m-d')) - ->groupBy($groupBy); + if ($enableChart) + { + foreach ([ENTITY_INVOICE, ENTITY_PAYMENT, ENTITY_CREDIT] as $entityType) + { + // SQLite does not support the YEAR(), MONTH(), WEEK() and similar functions. + // Let's see if SQLite is being used. + if (Config::get('database.connections.'.Config::get('database.default').'.driver') == 'sqlite') + { + // Replace the unsupported function with it's date format counterpart + switch ($groupBy) + { + case 'MONTH': + $dateFormat = '%m'; // returns 01-12 + break; + case 'WEEK': + $dateFormat = '%W'; // returns 00-53 + break; + case 'DAYOFYEAR': + $dateFormat = '%j'; // returns 001-366 + break; + default: + $dateFormat = '%m'; // MONTH by default + break; + } - if ($entityType == ENTITY_INVOICE) { + // Concatenate the year and the chosen timeframe (Month, Week or Day) + $timeframe = 'strftime("%Y", '.$entityType.'_date) || strftime("'.$dateFormat.'", '.$entityType.'_date)'; + } + else + { + // Supported by Laravel's other DBMS drivers (MySQL, MSSQL and PostgreSQL) + $timeframe = 'concat(YEAR('.$entityType.'_date), '.$groupBy.'('.$entityType.'_date))'; + } + + $records = DB::table($entityType.'s') + ->select(DB::raw('sum(amount) as total, '.$timeframe.' as '.$groupBy)) + ->where('account_id', '=', Auth::user()->account_id) + ->where($entityType.'s.is_deleted', '=', false) + ->where($entityType.'s.'.$entityType.'_date', '>=', $startDate->format('Y-m-d')) + ->where($entityType.'s.'.$entityType.'_date', '<=', $endDate->format('Y-m-d')) + ->groupBy($groupBy); + + if ($entityType == ENTITY_INVOICE) + { $records->where('is_quote', '=', false) ->where('is_recurring', '=', false); } $totals = $records->lists('total'); - $dates = $records->lists($groupBy); - $data = array_combine($dates, $totals); + $dates = $records->lists($groupBy); + $data = array_combine($dates, $totals); $padding = $groupBy == 'DAYOFYEAR' ? 'day' : ($groupBy == 'WEEK' ? 'week' : 'month'); $endDate->modify('+1 '.$padding); $interval = new DateInterval('P1'.substr($groupBy, 0, 1)); - $period = new DatePeriod($startDate, $interval, $endDate); + $period = new DatePeriod($startDate, $interval, $endDate); $endDate->modify('-1 '.$padding); $totals = []; - foreach ($period as $d) { + foreach ($period as $d) + { $dateFormat = $groupBy == 'DAYOFYEAR' ? 'z' : ($groupBy == 'WEEK' ? 'W' : 'n'); $date = $d->format('Y'.$dateFormat); $totals[] = isset($data[$date]) ? $data[$date] : 0; - - if ($entityType == ENTITY_INVOICE) { + + if ($entityType == ENTITY_INVOICE) + { $labelFormat = $groupBy == 'DAYOFYEAR' ? 'j' : ($groupBy == 'WEEK' ? 'W' : 'F'); - $label = $d->format($labelFormat); + $label = $d->format($labelFormat); $labels[] = $label; } } - + $max = max($totals); - if ($max > 0) { + if ($max > 0) + { $datasets[] = [ 'totals' => $totals, 'colors' => $entityType == ENTITY_INVOICE ? '78,205,196' : ($entityType == ENTITY_CREDIT ? '199,244,100' : '255,107,107'), From 813b169006efb9069689aedee86881d0d36e6425 Mon Sep 17 00:00:00 2001 From: Patrick Samson Date: Thu, 18 Jun 2015 19:30:41 -0400 Subject: [PATCH 05/62] Fixed format_money() with empty string as value --- app/Libraries/Utils.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 94025347fcda..3ac30c47b5e5 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -251,6 +251,10 @@ class Utils $currency = Currency::find(1); } + if (!$value) { + $value = 0; + } + Cache::add('currency', $currency, DEFAULT_QUERY_CACHE); return $currency->symbol.number_format($value, $currency->precision, $currency->decimal_separator, $currency->thousand_separator); @@ -352,7 +356,7 @@ class Utils $timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE); $format = Session::get(SESSION_DATETIME_FORMAT, DEFAULT_DATETIME_FORMAT); - + $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', $date); $dateTime->setTimeZone(new DateTimeZone($timezone)); From 8b52cd10691951d4dde1d689b57140acb04e3e7e Mon Sep 17 00:00:00 2001 From: Patrick Samson Date: Thu, 18 Jun 2015 19:58:06 -0400 Subject: [PATCH 06/62] Improved French translation --- resources/lang/fr/texts.php | 44 ++++++------- resources/lang/fr_CA/texts.php | 112 ++++++++++++++++----------------- 2 files changed, 78 insertions(+), 78 deletions(-) diff --git a/resources/lang/fr/texts.php b/resources/lang/fr/texts.php index 010c5557709c..8ded07fc32f4 100644 --- a/resources/lang/fr/texts.php +++ b/resources/lang/fr/texts.php @@ -1,4 +1,4 @@ - 'Facture #', 'po_number' => 'Numéro du bon de commande', 'po_number_short' => 'Bon de commande #', - 'frequency_id' => 'Fréquence', + 'frequency_id' => 'Fréquence', 'discount' => 'Remise', 'taxes' => 'Taxes', 'tax' => 'Taxe', - 'item' => 'Article', + 'item' => 'Article', 'description' => 'Description', 'unit_cost' => 'Coût unitaire', 'quantity' => 'Quantité', @@ -117,11 +117,11 @@ return array( 'billed_client' => 'client facturé', 'billed_clients' => 'clients facturés', 'active_client' => 'client actif', - 'active_clients' => 'clients actifs', + 'active_clients' => 'clients actifs', 'invoices_past_due' => 'Date limite de paiement dépassée', 'upcoming_invoices' => 'Factures à venir', 'average_invoice' => 'Moyenne de facturation', - + // list pages 'archive' => 'Archiver', 'delete' => 'Supprimer', @@ -276,7 +276,7 @@ return array( // Payment page 'secure_payment' => 'Paiement sécurisé', 'card_number' => 'Numéro de carte', - 'expiration_month' => 'Mois d\'expiration', + 'expiration_month' => 'Mois d\'expiration', 'expiration_year' => 'Année d\'expiration', 'cvv' => 'CVV', @@ -297,8 +297,8 @@ return array( 'remove_logo_link' => 'Cliquez ici', ], - 'logout' => 'Se déconnecter', - 'sign_up_to_save' => 'Connectez vous pour sauvegarder votre travail', + 'logout' => 'Se déconnecter', + 'sign_up_to_save' => 'Connectez vous pour sauvegarder votre travail', 'agree_to_terms' =>'J\'accepte les conditions d\'utilisation d\'Invoice ninja :terms', 'terms_of_service' => 'Conditions d\'utilisation', 'email_taken' => 'L\'adresse courriel existe déjà', @@ -319,7 +319,7 @@ return array( 'field_label' => 'Nom du champ', 'field_value' => 'Valeur du champ', 'edit' => 'Éditer', - 'view_as_recipient' => 'Voir en tant que destinataire', + 'view_as_recipient' => 'Voir en tant que destinataire', // product management 'product_library' => 'Inventaire', @@ -387,7 +387,7 @@ return array( 'notification_quote_sent_subject' => 'Le devis :invoice a été envoyé à :client', 'notification_quote_viewed_subject' => 'Le devis :invoice a été visionné par :client', 'notification_quote_sent' => 'Le devis :invoice de :amount a été envoyé au client :client.', - 'notification_quote_viewed' => 'Le devis :invoice de :amount a été visioné par le client :client.', + 'notification_quote_viewed' => 'Le devis :invoice de :amount a été visioné par le client :client.', 'session_expired' => 'Votre session a expiré.', @@ -426,7 +426,7 @@ return array( 'sample_data' => 'Données fictives présentées', 'hide' => 'Cacher', 'new_version_available' => 'Une nouvelle version de :releases_link est disponible. Vous utilisez v:user_version, la plus récente est v:latest_version', - + 'invoice_settings' => 'Paramètres des factures', 'invoice_number_prefix' => 'Préfixe du numéro de facture', @@ -436,7 +436,7 @@ return array( 'share_invoice_counter' => 'Partager le compteur de facture', 'invoice_issued_to' => 'Facture destinée à', 'invalid_counter' => 'Pour éviter un éventuel conflit, merci de définir un préfixe pour le numéro de facture ou pour le numéro de devis', - 'mark_sent' => 'Marquer comme envoyé', + 'mark_sent' => 'Marquer comme envoyé', 'gateway_help_1' => ':link to sign up for Authorize.net.', 'gateway_help_2' => ':link to sign up for Authorize.net.', @@ -452,7 +452,7 @@ return array( 'more_designs_self_host_text' => '', 'buy' => 'Acheter', 'bought_designs' => 'Les nouveaux modèles ont été ajoutés avec succès', - + 'sent' => 'envoyé', 'timesheets' => 'Feuilles de temps', @@ -460,7 +460,7 @@ return array( 'payment_cvv' => '*Numéro à 3 ou 4 chiffres au dos de votre carte', 'payment_footer1' => '*L\'adresse de facturation doit correspondre à celle enregistrée avec votre carte bancaire', 'payment_footer2' => '*Merci de cliquer sur "Payer maintenant" une seule fois. Le processus peut prendre jusqu\'à 1 minute.', - 'vat_number' => 'Numéro de TVA', + 'vat_number' => 'Numéro de TVA', 'id_number' => 'Numéro ID', 'white_label_link' => 'Marque blanche', @@ -485,7 +485,7 @@ return array( 'reason_for_canceling' => 'Aidez nous à améliorer notre site en nous disant pourquoi vous partez.', 'discount_percent' => 'Pourcent', 'discount_amount' => 'Montant', - + 'invoice_history' => 'Historique des factures', 'quote_history' => 'Historique des devis', 'current_version' => 'Version courante', @@ -503,7 +503,7 @@ return array( 'payment_email' => 'Email de paiement', 'quote_email' => 'Email de déclaration', 'reset_all' => 'Réinitialiser', - 'approve' => 'Accepter', + 'approve' => 'Accepter', 'token_billing_type_id' => 'Token Billing', 'token_billing_help' => 'Enables you to store credit cards with your gateway, and charge them at a later date.', @@ -527,7 +527,7 @@ return array( 'order_overview' => 'Order overview', 'match_address' => '*Address must match address associated with credit card.', 'click_once' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', - + 'default_invoice_footer' => 'Définir par défaut', 'invoice_footer' => 'Pied de facture', 'save_as_default_footer' => 'Définir comme pied de facture par défatu', @@ -577,7 +577,7 @@ return array( 'notification_quote_approved' => 'The following client :client approved Quote :invoice for :amount.', 'resend_confirmation' => 'Resend confirmation email', 'confirmation_resent' => 'The confirmation email was resent', - + 'gateway_help_42' => ':link to sign up for BitPay.
Note: use a Legacy API Key, not an API token.', 'payment_type_credit_card' => 'Carte de crédit', 'payment_type_paypal' => 'PayPal', @@ -585,7 +585,7 @@ return array( 'knowledge_base' => 'Base de connaissances', 'partial' => 'Partiel', 'partial_remaining' => ':partial de :balance', - + 'more_fields' => 'Plus de champs', 'less_fields' => 'Moins de champs', 'client_name' => 'Nom du client', @@ -597,7 +597,7 @@ return array( 'view_documentation' => 'Voir documentation', 'app_title' => 'Free Open-Source Online Invoicing', 'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.', - + 'rows' => 'lignes', 'www' => 'www', 'logo' => 'Logo', @@ -628,7 +628,7 @@ return array( 'archive_task' => 'Archiver tâche', 'restore_task' => 'Restaurer tâche', 'delete_task' => 'Supprimer tâche', - 'stop_task' => 'Arrêter tâcher', + 'stop_task' => 'Arrêter tâche', 'time' => 'Temps', 'start' => 'Début', 'stop' => 'Fin', @@ -682,7 +682,7 @@ return array( 'resume' => 'Resume', 'break_duration' => 'Break', - 'edit_details' => 'Editer détails', + 'edit_details' => 'Modifier', 'work' => 'Travail', 'timezone_unset' => 'Please :link to set your timezone', 'click_here' => 'cliquer ici', diff --git a/resources/lang/fr_CA/texts.php b/resources/lang/fr_CA/texts.php index 3e808ab19e7f..71b75ac7975a 100644 --- a/resources/lang/fr_CA/texts.php +++ b/resources/lang/fr_CA/texts.php @@ -1,4 +1,4 @@ - 'Facture #', 'po_number' => 'Numéro du bon de commande', 'po_number_short' => 'Bon de commande #', - 'frequency_id' => 'Fréquence', + 'frequency_id' => 'Fréquence', 'discount' => 'Remise', 'taxes' => 'Taxes', 'tax' => 'Taxe', - 'item' => 'Article', + 'item' => 'Article', 'description' => 'Description', 'unit_cost' => 'Coût unitaire', 'quantity' => 'Quantité', @@ -117,11 +117,11 @@ return array( 'billed_client' => 'client facturé', 'billed_clients' => 'clients facturés', 'active_client' => 'client actif', - 'active_clients' => 'clients actifs', + 'active_clients' => 'clients actifs', 'invoices_past_due' => 'Date limite de paiement dépassée', 'upcoming_invoices' => 'Factures à venir', 'average_invoice' => 'Moyenne de facturation', - + // list pages 'archive' => 'Archiver', 'delete' => 'Supprimer', @@ -276,7 +276,7 @@ return array( // Payment page 'secure_payment' => 'Paiement sécurisé', 'card_number' => 'Numéro de carte', - 'expiration_month' => 'Mois d\'expiration', + 'expiration_month' => 'Mois d\'expiration', 'expiration_year' => 'Année d\'expiration', 'cvv' => 'CVV', @@ -297,8 +297,8 @@ return array( 'remove_logo_link' => 'Cliquez ici', ], - 'logout' => 'Se déconnecter', - 'sign_up_to_save' => 'Connectez vous pour sauvegarder votre travail', + 'logout' => 'Se déconnecter', + 'sign_up_to_save' => 'Connectez vous pour sauvegarder votre travail', 'agree_to_terms' =>'J\'accepte les conditions d\'utilisation d\'Invoice ninja :terms', 'terms_of_service' => 'Conditions d\'utilisation', 'email_taken' => 'L\'adresse courriel existe déjà', @@ -319,7 +319,7 @@ return array( 'field_label' => 'Nom du champ', 'field_value' => 'Valeur du champ', 'edit' => 'Éditer', - 'view_as_recipient' => 'Voir en tant que destinataire', + 'view_as_recipient' => 'Voir en tant que destinataire', // product management 'product_library' => 'Inventaire', @@ -387,7 +387,7 @@ return array( 'notification_quote_sent_subject' => 'Le devis :invoice a été envoyé à :client', 'notification_quote_viewed_subject' => 'Le devis :invoice a été visionné par :client', 'notification_quote_sent' => 'Le devis :invoice de :amount a été envoyé au client :client.', - 'notification_quote_viewed' => 'Le devis :invoice de :amount a été visioné par le client :client.', + 'notification_quote_viewed' => 'Le devis :invoice de :amount a été visioné par le client :client.', 'session_expired' => 'Votre session a expiré.', @@ -426,7 +426,7 @@ return array( 'sample_data' => 'Données fictives présentées', 'hide' => 'Cacher', 'new_version_available' => 'Une nouvelle version de :releases_link est disponible. Vous utilisez v:user_version, la plus récente est v:latest_version', - + 'invoice_settings' => 'Paramètres des factures', 'invoice_number_prefix' => 'Préfixe du numéro de facture', @@ -436,7 +436,7 @@ return array( 'share_invoice_counter' => 'Partager le compteur de facture', 'invoice_issued_to' => 'Facture destinée à', 'invalid_counter' => 'Pour éviter un éventuel conflit, merci de définir un préfixe pour le numéro de facture ou pour le numéro de devis', - 'mark_sent' => 'Marquer comme envoyé', + 'mark_sent' => 'Marquer comme envoyé', 'gateway_help_1' => ':link to sign up for Authorize.net.', 'gateway_help_2' => ':link to sign up for Authorize.net.', @@ -452,7 +452,7 @@ return array( 'more_designs_self_host_text' => '', 'buy' => 'Acheter', 'bought_designs' => 'Les nouveaux modèles ont été ajoutés avec succès', - + 'sent' => 'envoyé', 'timesheets' => 'Feuilles de temps', @@ -460,7 +460,7 @@ return array( 'payment_cvv' => '*This is the 3-4 digit number onthe back of your card', 'payment_footer1' => '*Billing address must match address associated with credit card.', 'payment_footer2' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', - 'vat_number' => 'Numéro de TVA', + 'vat_number' => 'Numéro de TVA', 'id_number' => 'Numéro ID', 'white_label_link' => 'Marque blanche', @@ -485,7 +485,7 @@ return array( 'reason_for_canceling' => 'Aidez nous à améliorer notre site en nous disant pourquoi vous partez.', 'discount_percent' => 'Pourcent', 'discount_amount' => 'Montant', - + 'invoice_history' => 'Historique des factures', 'quote_history' => 'Historique des devis', 'current_version' => 'Version courante', @@ -503,7 +503,7 @@ return array( 'payment_email' => 'Courriel de paiement', 'quote_email' => 'Courriel de devis', 'reset_all' => 'Tout remettre à zéro', - 'approve' => 'Approuver', + 'approve' => 'Approuver', 'token_billing_type_id' => 'Token Billing', 'token_billing_help' => 'Enables you to store credit cards with your gateway, and charge them at a later date.', @@ -527,7 +527,7 @@ return array( 'order_overview' => 'Order overview', 'match_address' => '*Address must match address associated with credit card.', 'click_once' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', - + 'default_invoice_footer' => 'Set default invoice footer', 'invoice_footer' => 'Invoice footer', 'save_as_default_footer' => 'Save as default footer', @@ -571,13 +571,13 @@ return array( 'send_email' => 'Send email', 'set_password' => 'Set Password', 'converted' => 'Converted', - + 'email_approved' => 'Email me when a quote is approved', 'notification_quote_approved_subject' => 'Quote :invoice was approved by :client', 'notification_quote_approved' => 'The following client :client approved Quote :invoice for :amount.', 'resend_confirmation' => 'Resend confirmation email', 'confirmation_resent' => 'The confirmation email was resent', - + 'gateway_help_42' => ':link to sign up for BitPay.
Note: use a Legacy API Key, not an API token.', 'payment_type_credit_card' => 'Credit card', 'payment_type_paypal' => 'PayPal', @@ -597,7 +597,7 @@ return array( 'view_documentation' => 'View Documentation', 'app_title' => 'Free Open-Source Online Invoicing', 'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.', - + 'rows' => 'rows', 'www' => 'www', 'logo' => 'Logo', @@ -619,50 +619,50 @@ return array( 'last_invoice_sent' => 'Last invoice sent :date', 'processed_updates' => 'Successfully completed update', - 'tasks' => 'Tasks', - 'new_task' => 'New Task', - 'start_time' => 'Start Time', - 'created_task' => 'Successfully created task', - 'updated_task' => 'Successfully updated task', + 'tasks' => 'Tâches', + 'new_task' => 'Nouvelle Tâche', + 'start_time' => 'Démarrée à', + 'created_task' => 'Tâche créée avec succès', + 'updated_task' => 'Tâche modifiée avec succès', 'edit_task' => 'Edit Task', - 'archive_task' => 'Archive Task', - 'restore_task' => 'Restore Task', - 'delete_task' => 'Delete Task', - 'stop_task' => 'Stop Task', + 'archive_task' => 'Archiver la Tâche', + 'restore_task' => 'Restaurer la Tâche', + 'delete_task' => 'Supprimer la Tâche', + 'stop_task' => 'Arrêter la Tâche', 'time' => 'Time', - 'start' => 'Start', - 'stop' => 'Stop', + 'start' => 'Démarrer', + 'stop' => 'Arrêter', 'now' => 'Now', 'timer' => 'Timer', 'manual' => 'Manual', 'date_and_time' => 'Date & Time', - 'second' => 'second', - 'seconds' => 'seconds', + 'second' => 'seconde', + 'seconds' => 'secondes', 'minute' => 'minute', 'minutes' => 'minutes', - 'hour' => 'hour', - 'hours' => 'hours', - 'task_details' => 'Task Details', - 'duration' => 'Duration', - 'end_time' => 'End Time', + 'hour' => 'heure', + 'hours' => 'heures', + 'task_details' => 'Détails de la Tâche', + 'duration' => 'Durée', + 'end_time' => 'Arrêtée à', 'end' => 'End', 'invoiced' => 'Invoiced', 'logged' => 'Logged', 'running' => 'Running', - 'task_error_multiple_clients' => 'The tasks can\'t belong to different clients', - 'task_error_running' => 'Please stop running tasks first', - 'task_error_invoiced' => 'Tasks have already been invoiced', - 'restored_task' => 'Successfully restored task', - 'archived_task' => 'Successfully archived task', - 'archived_tasks' => 'Successfully archived :count tasks', - 'deleted_task' => 'Successfully deleted task', - 'deleted_tasks' => 'Successfully deleted :count tasks', - 'create_task' => 'Create Task', - 'stopped_task' => 'Successfully stopped task', - 'invoice_task' => 'Invoice Task', + 'task_error_multiple_clients' => 'Une tâche ne peut appartenir à plusieurs clients', + 'task_error_running' => 'Merci d\'arrêter les tâches en cours', + 'task_error_invoiced' => 'Ces tâches ont déjà été facturées', + 'restored_task' => 'Tâche restaurée avec succès', + 'archived_task' => 'Tâche archivée avec succès', + 'archived_tasks' => ':count tâches archivées avec succès', + 'deleted_task' => 'Tâche supprimée avec succès', + 'deleted_tasks' => ':count tâches supprimées avec succès', + 'create_task' => 'Créer une Tâche', + 'stopped_task' => 'Tâche arrêtée avec succès', + 'invoice_task' => 'Facturer Tâche', 'invoice_labels' => 'Invoice Labels', - 'prefix' => 'Prefix', - 'counter' => 'Counter', + 'prefix' => 'Préfixe', + 'counter' => 'Compteur', 'payment_type_dwolla' => 'Dwolla', 'gateway_help_43' => ':link to sign up for Dwolla.', @@ -681,12 +681,12 @@ return array( 'pro_plan_feature7' => 'Customize Invoice Field Titles & Numbering', 'pro_plan_feature8' => 'Option to Attach PDFs to Client Emails', - 'resume' => 'Resume', - 'break_duration' => 'Break', - 'edit_details' => 'Edit Details', - 'work' => 'Work', + 'resume' => 'Continuer', + 'break_duration' => 'Pause', + 'edit_details' => 'Modifier', + 'work' => 'Travail', 'timezone_unset' => 'Please :link to set your timezone', - 'click_here' => 'click here', + 'click_here' => 'cliquer içi', 'email_receipt' => 'Email payment receipt to the client', 'created_payment_emailed_client' => 'Successfully created payment and emailed client', From b425ca9ba04daf0ffec497460a5d945100bd996f Mon Sep 17 00:00:00 2001 From: trigras Date: Fri, 10 Jul 2015 23:06:06 +0300 Subject: [PATCH 07/62] datepicker localization --- Gruntfile.js | 12 +- public/js/built.js | 1789 +----------------------------- resources/views/master.blade.php | 4 + 3 files changed, 27 insertions(+), 1778 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 3e358a158ca0..4de932ca45a6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -51,7 +51,17 @@ module.exports = function(grunt) { 'public/vendor/knockout-mapping/build/output/knockout.mapping-latest.js', 'public/vendor/knockout-sortable/build/knockout-sortable.min.js', 'public/vendor/underscore/underscore.js', - 'public/vendor/bootstrap-datepicker/dist/js/bootstrap-datepicker.js', + 'public/vendor/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js', + 'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.de.min.js', + 'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.da.min.js', + 'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.pt-BR.min.js', + 'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.nl.min.js', + 'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.fr.min.js', + 'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.it.min.js', + 'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.lt.min.js', + 'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.no.min.js', + 'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.es.min.js', + 'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.sv.min.js', 'public/vendor/typeahead.js/dist/typeahead.min.js', 'public/vendor/accounting/accounting.min.js', 'public/vendor/spectrum/spectrum.js', diff --git a/public/js/built.js b/public/js/built.js index ba3b95aa1b05..05b302aedefe 100644 --- a/public/js/built.js +++ b/public/js/built.js @@ -27158,1783 +27158,18 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p * Copyright 2012 Stefan Petre * Improvements by Andrew Rowls * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - */(function($, undefined){ - - function UTCDate(){ - return new Date(Date.UTC.apply(Date, arguments)); - } - function UTCToday(){ - var today = new Date(); - return UTCDate(today.getFullYear(), today.getMonth(), today.getDate()); - } - function isUTCEquals(date1, date2) { - return ( - date1.getUTCFullYear() === date2.getUTCFullYear() && - date1.getUTCMonth() === date2.getUTCMonth() && - date1.getUTCDate() === date2.getUTCDate() - ); - } - function alias(method){ - return function(){ - return this[method].apply(this, arguments); - }; - } - - var DateArray = (function(){ - var extras = { - get: function(i){ - return this.slice(i)[0]; - }, - contains: function(d){ - // Array.indexOf is not cross-browser; - // $.inArray doesn't work with Dates - var val = d && d.valueOf(); - for (var i=0, l=this.length; i < l; i++) - if (this[i].valueOf() === val) - return i; - return -1; - }, - remove: function(i){ - this.splice(i,1); - }, - replace: function(new_array){ - if (!new_array) - return; - if (!$.isArray(new_array)) - new_array = [new_array]; - this.clear(); - this.push.apply(this, new_array); - }, - clear: function(){ - this.length = 0; - }, - copy: function(){ - var a = new DateArray(); - a.replace(this); - return a; - } - }; - - return function(){ - var a = []; - a.push.apply(a, arguments); - $.extend(a, extras); - return a; - }; - })(); - - - // Picker object - - var Datepicker = function(element, options){ - this._process_options(options); - - this.dates = new DateArray(); - this.viewDate = this.o.defaultViewDate; - this.focusDate = null; - - this.element = $(element); - this.isInline = false; - this.isInput = this.element.is('input'); - this.component = this.element.hasClass('date') ? this.element.find('.add-on, .input-group-addon, .btn') : false; - this.hasInput = this.component && this.element.find('input').length; - if (this.component && this.component.length === 0) - this.component = false; - - this.picker = $(DPGlobal.template); - this._buildEvents(); - this._attachEvents(); - - if (this.isInline){ - this.picker.addClass('datepicker-inline').appendTo(this.element); - } - else { - this.picker.addClass('datepicker-dropdown dropdown-menu'); - } - - if (this.o.rtl){ - this.picker.addClass('datepicker-rtl'); - } - - this.viewMode = this.o.startView; - - if (this.o.calendarWeeks) - this.picker.find('tfoot .today, tfoot .clear') - .attr('colspan', function(i, val){ - return parseInt(val) + 1; - }); - - this._allow_update = false; - - this.setStartDate(this._o.startDate); - this.setEndDate(this._o.endDate); - this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled); - this.setDatesDisabled(this.o.datesDisabled); - - this.fillDow(); - this.fillMonths(); - - this._allow_update = true; - - this.update(); - this.showMode(); - - if (this.isInline){ - this.show(); - } - }; - - Datepicker.prototype = { - constructor: Datepicker, - - _process_options: function(opts){ - // Store raw options for reference - this._o = $.extend({}, this._o, opts); - // Processed options - var o = this.o = $.extend({}, this._o); - - // Check if "de-DE" style date is available, if not language should - // fallback to 2 letter code eg "de" - var lang = o.language; - if (!dates[lang]){ - lang = lang.split('-')[0]; - if (!dates[lang]) - lang = defaults.language; - } - o.language = lang; - - switch (o.startView){ - case 2: - case 'decade': - o.startView = 2; - break; - case 1: - case 'year': - o.startView = 1; - break; - default: - o.startView = 0; - } - - switch (o.minViewMode){ - case 1: - case 'months': - o.minViewMode = 1; - break; - case 2: - case 'years': - o.minViewMode = 2; - break; - default: - o.minViewMode = 0; - } - - o.startView = Math.max(o.startView, o.minViewMode); - - // true, false, or Number > 0 - if (o.multidate !== true){ - o.multidate = Number(o.multidate) || false; - if (o.multidate !== false) - o.multidate = Math.max(0, o.multidate); - } - o.multidateSeparator = String(o.multidateSeparator); - - o.weekStart %= 7; - o.weekEnd = ((o.weekStart + 6) % 7); - - var format = DPGlobal.parseFormat(o.format); - if (o.startDate !== -Infinity){ - if (!!o.startDate){ - if (o.startDate instanceof Date) - o.startDate = this._local_to_utc(this._zero_time(o.startDate)); - else - o.startDate = DPGlobal.parseDate(o.startDate, format, o.language); - } - else { - o.startDate = -Infinity; - } - } - if (o.endDate !== Infinity){ - if (!!o.endDate){ - if (o.endDate instanceof Date) - o.endDate = this._local_to_utc(this._zero_time(o.endDate)); - else - o.endDate = DPGlobal.parseDate(o.endDate, format, o.language); - } - else { - o.endDate = Infinity; - } - } - - o.daysOfWeekDisabled = o.daysOfWeekDisabled||[]; - if (!$.isArray(o.daysOfWeekDisabled)) - o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/); - o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function(d){ - return parseInt(d, 10); - }); - - o.datesDisabled = o.datesDisabled||[]; - if (!$.isArray(o.datesDisabled)) { - var datesDisabled = []; - datesDisabled.push(DPGlobal.parseDate(o.datesDisabled, format, o.language)); - o.datesDisabled = datesDisabled; - } - o.datesDisabled = $.map(o.datesDisabled,function(d){ - return DPGlobal.parseDate(d, format, o.language); - }); - - var plc = String(o.orientation).toLowerCase().split(/\s+/g), - _plc = o.orientation.toLowerCase(); - plc = $.grep(plc, function(word){ - return /^auto|left|right|top|bottom$/.test(word); - }); - o.orientation = {x: 'auto', y: 'auto'}; - if (!_plc || _plc === 'auto') - ; // no action - else if (plc.length === 1){ - switch (plc[0]){ - case 'top': - case 'bottom': - o.orientation.y = plc[0]; - break; - case 'left': - case 'right': - o.orientation.x = plc[0]; - break; - } - } - else { - _plc = $.grep(plc, function(word){ - return /^left|right$/.test(word); - }); - o.orientation.x = _plc[0] || 'auto'; - - _plc = $.grep(plc, function(word){ - return /^top|bottom$/.test(word); - }); - o.orientation.y = _plc[0] || 'auto'; - } - if (o.defaultViewDate) { - var year = o.defaultViewDate.year || new Date().getFullYear(); - var month = o.defaultViewDate.month || 0; - var day = o.defaultViewDate.day || 1; - o.defaultViewDate = UTCDate(year, month, day); - } else { - o.defaultViewDate = UTCToday(); - } - o.showOnFocus = o.showOnFocus !== undefined ? o.showOnFocus : true; - }, - _events: [], - _secondaryEvents: [], - _applyEvents: function(evs){ - for (var i=0, el, ch, ev; i < evs.length; i++){ - el = evs[i][0]; - if (evs[i].length === 2){ - ch = undefined; - ev = evs[i][1]; - } - else if (evs[i].length === 3){ - ch = evs[i][1]; - ev = evs[i][2]; - } - el.on(ev, ch); - } - }, - _unapplyEvents: function(evs){ - for (var i=0, el, ev, ch; i < evs.length; i++){ - el = evs[i][0]; - if (evs[i].length === 2){ - ch = undefined; - ev = evs[i][1]; - } - else if (evs[i].length === 3){ - ch = evs[i][1]; - ev = evs[i][2]; - } - el.off(ev, ch); - } - }, - _buildEvents: function(){ - var events = { - keyup: $.proxy(function(e){ - if ($.inArray(e.keyCode, [27, 37, 39, 38, 40, 32, 13, 9]) === -1) - this.update(); - }, this), - keydown: $.proxy(this.keydown, this) - }; - - if (this.o.showOnFocus === true) { - events.focus = $.proxy(this.show, this); - } - - if (this.isInput) { // single input - this._events = [ - [this.element, events] - ]; - } - else if (this.component && this.hasInput) { // component: input + button - this._events = [ - // For components that are not readonly, allow keyboard nav - [this.element.find('input'), events], - [this.component, { - click: $.proxy(this.show, this) - }] - ]; - } - else if (this.element.is('div')){ // inline datepicker - this.isInline = true; - } - else { - this._events = [ - [this.element, { - click: $.proxy(this.show, this) - }] - ]; - } - this._events.push( - // Component: listen for blur on element descendants - [this.element, '*', { - blur: $.proxy(function(e){ - this._focused_from = e.target; - }, this) - }], - // Input: listen for blur on element - [this.element, { - blur: $.proxy(function(e){ - this._focused_from = e.target; - }, this) - }] - ); - - this._secondaryEvents = [ - [this.picker, { - click: $.proxy(this.click, this) - }], - [$(window), { - resize: $.proxy(this.place, this) - }], - [$(document), { - 'mousedown touchstart': $.proxy(function(e){ - // Clicked outside the datepicker, hide it - if (!( - this.element.is(e.target) || - this.element.find(e.target).length || - this.picker.is(e.target) || - this.picker.find(e.target).length - )){ - this.hide(); - } - }, this) - }] - ]; - }, - _attachEvents: function(){ - this._detachEvents(); - this._applyEvents(this._events); - }, - _detachEvents: function(){ - this._unapplyEvents(this._events); - }, - _attachSecondaryEvents: function(){ - this._detachSecondaryEvents(); - this._applyEvents(this._secondaryEvents); - }, - _detachSecondaryEvents: function(){ - this._unapplyEvents(this._secondaryEvents); - }, - _trigger: function(event, altdate){ - var date = altdate || this.dates.get(-1), - local_date = this._utc_to_local(date); - - this.element.trigger({ - type: event, - date: local_date, - dates: $.map(this.dates, this._utc_to_local), - format: $.proxy(function(ix, format){ - if (arguments.length === 0){ - ix = this.dates.length - 1; - format = this.o.format; - } - else if (typeof ix === 'string'){ - format = ix; - ix = this.dates.length - 1; - } - format = format || this.o.format; - var date = this.dates.get(ix); - return DPGlobal.formatDate(date, format, this.o.language); - }, this) - }); - }, - - show: function(){ - if (this.element.attr('readonly') && this.o.enableOnReadonly === false) - return; - if (!this.isInline) - this.picker.appendTo(this.o.container); - this.place(); - this.picker.show(); - this._attachSecondaryEvents(); - this._trigger('show'); - if ((window.navigator.msMaxTouchPoints || 'ontouchstart' in document) && this.o.disableTouchKeyboard) { - $(this.element).blur(); - } - return this; - }, - - hide: function(){ - if (this.isInline) - return this; - if (!this.picker.is(':visible')) - return this; - this.focusDate = null; - this.picker.hide().detach(); - this._detachSecondaryEvents(); - this.viewMode = this.o.startView; - this.showMode(); - - if ( - this.o.forceParse && - ( - this.isInput && this.element.val() || - this.hasInput && this.element.find('input').val() - ) - ) - this.setValue(); - this._trigger('hide'); - return this; - }, - - remove: function(){ - this.hide(); - this._detachEvents(); - this._detachSecondaryEvents(); - this.picker.remove(); - delete this.element.data().datepicker; - if (!this.isInput){ - delete this.element.data().date; - } - return this; - }, - - _utc_to_local: function(utc){ - return utc && new Date(utc.getTime() + (utc.getTimezoneOffset()*60000)); - }, - _local_to_utc: function(local){ - return local && new Date(local.getTime() - (local.getTimezoneOffset()*60000)); - }, - _zero_time: function(local){ - return local && new Date(local.getFullYear(), local.getMonth(), local.getDate()); - }, - _zero_utc_time: function(utc){ - return utc && new Date(Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate())); - }, - - getDates: function(){ - return $.map(this.dates, this._utc_to_local); - }, - - getUTCDates: function(){ - return $.map(this.dates, function(d){ - return new Date(d); - }); - }, - - getDate: function(){ - return this._utc_to_local(this.getUTCDate()); - }, - - getUTCDate: function(){ - var selected_date = this.dates.get(-1); - if (typeof selected_date !== 'undefined') { - return new Date(selected_date); - } else { - return null; - } - }, - - clearDates: function(){ - var element; - if (this.isInput) { - element = this.element; - } else if (this.component) { - element = this.element.find('input'); - } - - if (element) { - element.val('').change(); - } - - this.update(); - this._trigger('changeDate'); - - if (this.o.autoclose) { - this.hide(); - } - }, - setDates: function(){ - var args = $.isArray(arguments[0]) ? arguments[0] : arguments; - this.update.apply(this, args); - this._trigger('changeDate'); - this.setValue(); - return this; - }, - - setUTCDates: function(){ - var args = $.isArray(arguments[0]) ? arguments[0] : arguments; - this.update.apply(this, $.map(args, this._utc_to_local)); - this._trigger('changeDate'); - this.setValue(); - return this; - }, - - setDate: alias('setDates'), - setUTCDate: alias('setUTCDates'), - - setValue: function(){ - var formatted = this.getFormattedDate(); - if (!this.isInput){ - if (this.component){ - this.element.find('input').val(formatted).change(); - } - } - else { - this.element.val(formatted).change(); - } - return this; - }, - - getFormattedDate: function(format){ - if (format === undefined) - format = this.o.format; - - var lang = this.o.language; - return $.map(this.dates, function(d){ - return DPGlobal.formatDate(d, format, lang); - }).join(this.o.multidateSeparator); - }, - - setStartDate: function(startDate){ - this._process_options({startDate: startDate}); - this.update(); - this.updateNavArrows(); - return this; - }, - - setEndDate: function(endDate){ - this._process_options({endDate: endDate}); - this.update(); - this.updateNavArrows(); - return this; - }, - - setDaysOfWeekDisabled: function(daysOfWeekDisabled){ - this._process_options({daysOfWeekDisabled: daysOfWeekDisabled}); - this.update(); - this.updateNavArrows(); - return this; - }, - - setDatesDisabled: function(datesDisabled){ - this._process_options({datesDisabled: datesDisabled}); - this.update(); - this.updateNavArrows(); - }, - - place: function(){ - if (this.isInline) - return this; - var calendarWidth = this.picker.outerWidth(), - calendarHeight = this.picker.outerHeight(), - visualPadding = 10, - windowWidth = $(this.o.container).width(), - windowHeight = $(this.o.container).height(), - scrollTop = $(this.o.container).scrollTop(), - appendOffset = $(this.o.container).offset(); - - var parentsZindex = []; - this.element.parents().each(function(){ - var itemZIndex = $(this).css('z-index'); - if (itemZIndex !== 'auto' && itemZIndex !== 0) parentsZindex.push(parseInt(itemZIndex)); - }); - var zIndex = Math.max.apply(Math, parentsZindex) + 10; - var offset = this.component ? this.component.parent().offset() : this.element.offset(); - var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false); - var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false); - var left = offset.left - appendOffset.left, - top = offset.top - appendOffset.top; - - this.picker.removeClass( - 'datepicker-orient-top datepicker-orient-bottom '+ - 'datepicker-orient-right datepicker-orient-left' - ); - - if (this.o.orientation.x !== 'auto'){ - this.picker.addClass('datepicker-orient-' + this.o.orientation.x); - if (this.o.orientation.x === 'right') - left -= calendarWidth - width; - } - // auto x orientation is best-placement: if it crosses a window - // edge, fudge it sideways - else { - if (offset.left < 0) { - // component is outside the window on the left side. Move it into visible range - this.picker.addClass('datepicker-orient-left'); - left -= offset.left - visualPadding; - } else if (left + calendarWidth > windowWidth) { - // the calendar passes the widow right edge. Align it to component right side - this.picker.addClass('datepicker-orient-right'); - left = offset.left + width - calendarWidth; - } else { - // Default to left - this.picker.addClass('datepicker-orient-left'); - } - } - - // auto y orientation is best-situation: top or bottom, no fudging, - // decision based on which shows more of the calendar - var yorient = this.o.orientation.y, - top_overflow, bottom_overflow; - if (yorient === 'auto'){ - top_overflow = -scrollTop + top - calendarHeight; - bottom_overflow = scrollTop + windowHeight - (top + height + calendarHeight); - if (Math.max(top_overflow, bottom_overflow) === bottom_overflow) - yorient = 'top'; - else - yorient = 'bottom'; - } - this.picker.addClass('datepicker-orient-' + yorient); - if (yorient === 'top') - top += height; - else - top -= calendarHeight + parseInt(this.picker.css('padding-top')); - - if (this.o.rtl) { - var right = windowWidth - (left + width); - this.picker.css({ - top: top, - right: right, - zIndex: zIndex - }); - } else { - this.picker.css({ - top: top, - left: left, - zIndex: zIndex - }); - } - return this; - }, - - _allow_update: true, - update: function(){ - if (!this._allow_update) - return this; - - var oldDates = this.dates.copy(), - dates = [], - fromArgs = false; - if (arguments.length){ - $.each(arguments, $.proxy(function(i, date){ - if (date instanceof Date) - date = this._local_to_utc(date); - dates.push(date); - }, this)); - fromArgs = true; - } - else { - dates = this.isInput - ? this.element.val() - : this.element.data('date') || this.element.find('input').val(); - if (dates && this.o.multidate) - dates = dates.split(this.o.multidateSeparator); - else - dates = [dates]; - delete this.element.data().date; - } - - dates = $.map(dates, $.proxy(function(date){ - return DPGlobal.parseDate(date, this.o.format, this.o.language); - }, this)); - dates = $.grep(dates, $.proxy(function(date){ - return ( - date < this.o.startDate || - date > this.o.endDate || - !date - ); - }, this), true); - this.dates.replace(dates); - - if (this.dates.length) - this.viewDate = new Date(this.dates.get(-1)); - else if (this.viewDate < this.o.startDate) - this.viewDate = new Date(this.o.startDate); - else if (this.viewDate > this.o.endDate) - this.viewDate = new Date(this.o.endDate); - - if (fromArgs){ - // setting date by clicking - this.setValue(); - } - else if (dates.length){ - // setting date by typing - if (String(oldDates) !== String(this.dates)) - this._trigger('changeDate'); - } - if (!this.dates.length && oldDates.length) - this._trigger('clearDate'); - - this.fill(); - return this; - }, - - fillDow: function(){ - var dowCnt = this.o.weekStart, - html = ''; - if (this.o.calendarWeeks){ - this.picker.find('.datepicker-days thead tr:first-child .datepicker-switch') - .attr('colspan', function(i, val){ - return parseInt(val) + 1; - }); - var cell = ' '; - html += cell; - } - while (dowCnt < this.o.weekStart + 7){ - html += ''+dates[this.o.language].daysMin[(dowCnt++)%7]+''; - } - html += ''; - this.picker.find('.datepicker-days thead').append(html); - }, - - fillMonths: function(){ - var html = '', - i = 0; - while (i < 12){ - html += ''+dates[this.o.language].monthsShort[i++]+''; - } - this.picker.find('.datepicker-months td').html(html); - }, - - setRange: function(range){ - if (!range || !range.length) - delete this.range; - else - this.range = $.map(range, function(d){ - return d.valueOf(); - }); - this.fill(); - }, - - getClassNames: function(date){ - var cls = [], - year = this.viewDate.getUTCFullYear(), - month = this.viewDate.getUTCMonth(), - today = new Date(); - if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)){ - cls.push('old'); - } - else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)){ - cls.push('new'); - } - if (this.focusDate && date.valueOf() === this.focusDate.valueOf()) - cls.push('focused'); - // Compare internal UTC date with local today, not UTC today - if (this.o.todayHighlight && - date.getUTCFullYear() === today.getFullYear() && - date.getUTCMonth() === today.getMonth() && - date.getUTCDate() === today.getDate()){ - cls.push('today'); - } - if (this.dates.contains(date) !== -1) - cls.push('active'); - if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate || - $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1){ - cls.push('disabled'); - } - if (this.o.datesDisabled.length > 0 && - $.grep(this.o.datesDisabled, function(d){ - return isUTCEquals(date, d); }).length > 0) { - cls.push('disabled', 'disabled-date'); - } - - if (this.range){ - if (date > this.range[0] && date < this.range[this.range.length-1]){ - cls.push('range'); - } - if ($.inArray(date.valueOf(), this.range) !== -1){ - cls.push('selected'); - } - } - return cls; - }, - - fill: function(){ - var d = new Date(this.viewDate), - year = d.getUTCFullYear(), - month = d.getUTCMonth(), - startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity, - startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity, - endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity, - endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity, - todaytxt = dates[this.o.language].today || dates['en'].today || '', - cleartxt = dates[this.o.language].clear || dates['en'].clear || '', - tooltip; - if (isNaN(year) || isNaN(month)) - return; - this.picker.find('.datepicker-days thead .datepicker-switch') - .text(dates[this.o.language].months[month]+' '+year); - this.picker.find('tfoot .today') - .text(todaytxt) - .toggle(this.o.todayBtn !== false); - this.picker.find('tfoot .clear') - .text(cleartxt) - .toggle(this.o.clearBtn !== false); - this.updateNavArrows(); - this.fillMonths(); - var prevMonth = UTCDate(year, month-1, 28), - day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); - prevMonth.setUTCDate(day); - prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7); - var nextMonth = new Date(prevMonth); - nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); - nextMonth = nextMonth.valueOf(); - var html = []; - var clsName; - while (prevMonth.valueOf() < nextMonth){ - if (prevMonth.getUTCDay() === this.o.weekStart){ - html.push(''); - if (this.o.calendarWeeks){ - // ISO 8601: First week contains first thursday. - // ISO also states week starts on Monday, but we can be more abstract here. - var - // Start of current week: based on weekstart/current date - ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5), - // Thursday of this week - th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5), - // First Thursday of year, year from thursday - yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5), - // Calendar week: ms between thursdays, div ms per day, div 7 days - calWeek = (th - yth) / 864e5 / 7 + 1; - html.push(''+ calWeek +''); - - } - } - clsName = this.getClassNames(prevMonth); - clsName.push('day'); - - if (this.o.beforeShowDay !== $.noop){ - var before = this.o.beforeShowDay(this._utc_to_local(prevMonth)); - if (before === undefined) - before = {}; - else if (typeof(before) === 'boolean') - before = {enabled: before}; - else if (typeof(before) === 'string') - before = {classes: before}; - if (before.enabled === false) - clsName.push('disabled'); - if (before.classes) - clsName = clsName.concat(before.classes.split(/\s+/)); - if (before.tooltip) - tooltip = before.tooltip; - } - - clsName = $.unique(clsName); - html.push(''+prevMonth.getUTCDate() + ''); - tooltip = null; - if (prevMonth.getUTCDay() === this.o.weekEnd){ - html.push(''); - } - prevMonth.setUTCDate(prevMonth.getUTCDate()+1); - } - this.picker.find('.datepicker-days tbody').empty().append(html.join('')); - - var months = this.picker.find('.datepicker-months') - .find('th:eq(1)') - .text(year) - .end() - .find('span').removeClass('active'); - - $.each(this.dates, function(i, d){ - if (d.getUTCFullYear() === year) - months.eq(d.getUTCMonth()).addClass('active'); - }); - - if (year < startYear || year > endYear){ - months.addClass('disabled'); - } - if (year === startYear){ - months.slice(0, startMonth).addClass('disabled'); - } - if (year === endYear){ - months.slice(endMonth+1).addClass('disabled'); - } - - if (this.o.beforeShowMonth !== $.noop){ - var that = this; - $.each(months, function(i, month){ - if (!$(month).hasClass('disabled')) { - var moDate = new Date(year, i, 1); - var before = that.o.beforeShowMonth(moDate); - if (before === false) - $(month).addClass('disabled'); - } - }); - } - - html = ''; - year = parseInt(year/10, 10) * 10; - var yearCont = this.picker.find('.datepicker-years') - .find('th:eq(1)') - .text(year + '-' + (year + 9)) - .end() - .find('td'); - year -= 1; - var years = $.map(this.dates, function(d){ - return d.getUTCFullYear(); - }), - classes; - for (var i = -1; i < 11; i++){ - classes = ['year']; - if (i === -1) - classes.push('old'); - else if (i === 10) - classes.push('new'); - if ($.inArray(year, years) !== -1) - classes.push('active'); - if (year < startYear || year > endYear) - classes.push('disabled'); - html += '' + year + ''; - year += 1; - } - yearCont.html(html); - }, - - updateNavArrows: function(){ - if (!this._allow_update) - return; - - var d = new Date(this.viewDate), - year = d.getUTCFullYear(), - month = d.getUTCMonth(); - switch (this.viewMode){ - case 0: - if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()){ - this.picker.find('.prev').css({visibility: 'hidden'}); - } - else { - this.picker.find('.prev').css({visibility: 'visible'}); - } - if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()){ - this.picker.find('.next').css({visibility: 'hidden'}); - } - else { - this.picker.find('.next').css({visibility: 'visible'}); - } - break; - case 1: - case 2: - if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()){ - this.picker.find('.prev').css({visibility: 'hidden'}); - } - else { - this.picker.find('.prev').css({visibility: 'visible'}); - } - if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()){ - this.picker.find('.next').css({visibility: 'hidden'}); - } - else { - this.picker.find('.next').css({visibility: 'visible'}); - } - break; - } - }, - - click: function(e){ - e.preventDefault(); - var target = $(e.target).closest('span, td, th'), - year, month, day; - if (target.length === 1){ - switch (target[0].nodeName.toLowerCase()){ - case 'th': - switch (target[0].className){ - case 'datepicker-switch': - this.showMode(1); - break; - case 'prev': - case 'next': - var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1); - switch (this.viewMode){ - case 0: - this.viewDate = this.moveMonth(this.viewDate, dir); - this._trigger('changeMonth', this.viewDate); - break; - case 1: - case 2: - this.viewDate = this.moveYear(this.viewDate, dir); - if (this.viewMode === 1) - this._trigger('changeYear', this.viewDate); - break; - } - this.fill(); - break; - case 'today': - var date = new Date(); - date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); - - this.showMode(-2); - var which = this.o.todayBtn === 'linked' ? null : 'view'; - this._setDate(date, which); - break; - case 'clear': - this.clearDates(); - break; - } - break; - case 'span': - if (!target.hasClass('disabled')){ - this.viewDate.setUTCDate(1); - if (target.hasClass('month')){ - day = 1; - month = target.parent().find('span').index(target); - year = this.viewDate.getUTCFullYear(); - this.viewDate.setUTCMonth(month); - this._trigger('changeMonth', this.viewDate); - if (this.o.minViewMode === 1){ - this._setDate(UTCDate(year, month, day)); - } - } - else { - day = 1; - month = 0; - year = parseInt(target.text(), 10)||0; - this.viewDate.setUTCFullYear(year); - this._trigger('changeYear', this.viewDate); - if (this.o.minViewMode === 2){ - this._setDate(UTCDate(year, month, day)); - } - } - this.showMode(-1); - this.fill(); - } - break; - case 'td': - if (target.hasClass('day') && !target.hasClass('disabled')){ - day = parseInt(target.text(), 10)||1; - year = this.viewDate.getUTCFullYear(); - month = this.viewDate.getUTCMonth(); - if (target.hasClass('old')){ - if (month === 0){ - month = 11; - year -= 1; - } - else { - month -= 1; - } - } - else if (target.hasClass('new')){ - if (month === 11){ - month = 0; - year += 1; - } - else { - month += 1; - } - } - this._setDate(UTCDate(year, month, day)); - } - break; - } - } - if (this.picker.is(':visible') && this._focused_from){ - $(this._focused_from).focus(); - } - delete this._focused_from; - }, - - _toggle_multidate: function(date){ - var ix = this.dates.contains(date); - if (!date){ - this.dates.clear(); - } - - if (ix !== -1){ - if (this.o.multidate === true || this.o.multidate > 1 || this.o.toggleActive){ - this.dates.remove(ix); - } - } else if (this.o.multidate === false) { - this.dates.clear(); - this.dates.push(date); - } - else { - this.dates.push(date); - } - - if (typeof this.o.multidate === 'number') - while (this.dates.length > this.o.multidate) - this.dates.remove(0); - }, - - _setDate: function(date, which){ - if (!which || which === 'date') - this._toggle_multidate(date && new Date(date)); - if (!which || which === 'view') - this.viewDate = date && new Date(date); - - this.fill(); - this.setValue(); - if (!which || which !== 'view') { - this._trigger('changeDate'); - } - var element; - if (this.isInput){ - element = this.element; - } - else if (this.component){ - element = this.element.find('input'); - } - if (element){ - element.change(); - } - if (this.o.autoclose && (!which || which === 'date')){ - this.hide(); - } - }, - - moveMonth: function(date, dir){ - if (!date) - return undefined; - if (!dir) - return date; - var new_date = new Date(date.valueOf()), - day = new_date.getUTCDate(), - month = new_date.getUTCMonth(), - mag = Math.abs(dir), - new_month, test; - dir = dir > 0 ? 1 : -1; - if (mag === 1){ - test = dir === -1 - // If going back one month, make sure month is not current month - // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) - ? function(){ - return new_date.getUTCMonth() === month; - } - // If going forward one month, make sure month is as expected - // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) - : function(){ - return new_date.getUTCMonth() !== new_month; - }; - new_month = month + dir; - new_date.setUTCMonth(new_month); - // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 - if (new_month < 0 || new_month > 11) - new_month = (new_month + 12) % 12; - } - else { - // For magnitudes >1, move one month at a time... - for (var i=0; i < mag; i++) - // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)... - new_date = this.moveMonth(new_date, dir); - // ...then reset the day, keeping it in the new month - new_month = new_date.getUTCMonth(); - new_date.setUTCDate(day); - test = function(){ - return new_month !== new_date.getUTCMonth(); - }; - } - // Common date-resetting loop -- if date is beyond end of month, make it - // end of month - while (test()){ - new_date.setUTCDate(--day); - new_date.setUTCMonth(new_month); - } - return new_date; - }, - - moveYear: function(date, dir){ - return this.moveMonth(date, dir*12); - }, - - dateWithinRange: function(date){ - return date >= this.o.startDate && date <= this.o.endDate; - }, - - keydown: function(e){ - if (!this.picker.is(':visible')){ - if (e.keyCode === 27) // allow escape to hide and re-show picker - this.show(); - return; - } - var dateChanged = false, - dir, newDate, newViewDate, - focusDate = this.focusDate || this.viewDate; - switch (e.keyCode){ - case 27: // escape - if (this.focusDate){ - this.focusDate = null; - this.viewDate = this.dates.get(-1) || this.viewDate; - this.fill(); - } - else - this.hide(); - e.preventDefault(); - break; - case 37: // left - case 39: // right - if (!this.o.keyboardNavigation) - break; - dir = e.keyCode === 37 ? -1 : 1; - if (e.ctrlKey){ - newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir); - newViewDate = this.moveYear(focusDate, dir); - this._trigger('changeYear', this.viewDate); - } - else if (e.shiftKey){ - newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir); - newViewDate = this.moveMonth(focusDate, dir); - this._trigger('changeMonth', this.viewDate); - } - else { - newDate = new Date(this.dates.get(-1) || UTCToday()); - newDate.setUTCDate(newDate.getUTCDate() + dir); - newViewDate = new Date(focusDate); - newViewDate.setUTCDate(focusDate.getUTCDate() + dir); - } - if (this.dateWithinRange(newViewDate)){ - this.focusDate = this.viewDate = newViewDate; - this.setValue(); - this.fill(); - e.preventDefault(); - } - break; - case 38: // up - case 40: // down - if (!this.o.keyboardNavigation) - break; - dir = e.keyCode === 38 ? -1 : 1; - if (e.ctrlKey){ - newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir); - newViewDate = this.moveYear(focusDate, dir); - this._trigger('changeYear', this.viewDate); - } - else if (e.shiftKey){ - newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir); - newViewDate = this.moveMonth(focusDate, dir); - this._trigger('changeMonth', this.viewDate); - } - else { - newDate = new Date(this.dates.get(-1) || UTCToday()); - newDate.setUTCDate(newDate.getUTCDate() + dir * 7); - newViewDate = new Date(focusDate); - newViewDate.setUTCDate(focusDate.getUTCDate() + dir * 7); - } - if (this.dateWithinRange(newViewDate)){ - this.focusDate = this.viewDate = newViewDate; - this.setValue(); - this.fill(); - e.preventDefault(); - } - break; - case 32: // spacebar - // Spacebar is used in manually typing dates in some formats. - // As such, its behavior should not be hijacked. - break; - case 13: // enter - focusDate = this.focusDate || this.dates.get(-1) || this.viewDate; - if (this.o.keyboardNavigation) { - this._toggle_multidate(focusDate); - dateChanged = true; - } - this.focusDate = null; - this.viewDate = this.dates.get(-1) || this.viewDate; - this.setValue(); - this.fill(); - if (this.picker.is(':visible')){ - e.preventDefault(); - if (typeof e.stopPropagation === 'function') { - e.stopPropagation(); // All modern browsers, IE9+ - } else { - e.cancelBubble = true; // IE6,7,8 ignore "stopPropagation" - } - if (this.o.autoclose) - this.hide(); - } - break; - case 9: // tab - this.focusDate = null; - this.viewDate = this.dates.get(-1) || this.viewDate; - this.fill(); - this.hide(); - break; - } - if (dateChanged){ - if (this.dates.length) - this._trigger('changeDate'); - else - this._trigger('clearDate'); - var element; - if (this.isInput){ - element = this.element; - } - else if (this.component){ - element = this.element.find('input'); - } - if (element){ - element.change(); - } - } - }, - - showMode: function(dir){ - if (dir){ - this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir)); - } - this.picker - .children('div') - .hide() - .filter('.datepicker-' + DPGlobal.modes[this.viewMode].clsName) - .css('display', 'block'); - this.updateNavArrows(); - } - }; - - var DateRangePicker = function(element, options){ - this.element = $(element); - this.inputs = $.map(options.inputs, function(i){ - return i.jquery ? i[0] : i; - }); - delete options.inputs; - - datepickerPlugin.call($(this.inputs), options) - .bind('changeDate', $.proxy(this.dateUpdated, this)); - - this.pickers = $.map(this.inputs, function(i){ - return $(i).data('datepicker'); - }); - this.updateDates(); - }; - DateRangePicker.prototype = { - updateDates: function(){ - this.dates = $.map(this.pickers, function(i){ - return i.getUTCDate(); - }); - this.updateRanges(); - }, - updateRanges: function(){ - var range = $.map(this.dates, function(d){ - return d.valueOf(); - }); - $.each(this.pickers, function(i, p){ - p.setRange(range); - }); - }, - dateUpdated: function(e){ - // `this.updating` is a workaround for preventing infinite recursion - // between `changeDate` triggering and `setUTCDate` calling. Until - // there is a better mechanism. - if (this.updating) - return; - this.updating = true; - - var dp = $(e.target).data('datepicker'), - new_date = dp.getUTCDate(), - i = $.inArray(e.target, this.inputs), - j = i - 1, - k = i + 1, - l = this.inputs.length; - if (i === -1) - return; - - $.each(this.pickers, function(i, p){ - if (!p.getUTCDate()) - p.setUTCDate(new_date); - }); - - if (new_date < this.dates[j]){ - // Date being moved earlier/left - while (j >= 0 && new_date < this.dates[j]){ - this.pickers[j--].setUTCDate(new_date); - } - } - else if (new_date > this.dates[k]){ - // Date being moved later/right - while (k < l && new_date > this.dates[k]){ - this.pickers[k++].setUTCDate(new_date); - } - } - this.updateDates(); - - delete this.updating; - }, - remove: function(){ - $.map(this.pickers, function(p){ p.remove(); }); - delete this.element.data().datepicker; - } - }; - - function opts_from_el(el, prefix){ - // Derive options from element data-attrs - var data = $(el).data(), - out = {}, inkey, - replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'); - prefix = new RegExp('^' + prefix.toLowerCase()); - function re_lower(_,a){ - return a.toLowerCase(); - } - for (var key in data) - if (prefix.test(key)){ - inkey = key.replace(replace, re_lower); - out[inkey] = data[key]; - } - return out; - } - - function opts_from_locale(lang){ - // Derive options from locale plugins - var out = {}; - // Check if "de-DE" style date is available, if not language should - // fallback to 2 letter code eg "de" - if (!dates[lang]){ - lang = lang.split('-')[0]; - if (!dates[lang]) - return; - } - var d = dates[lang]; - $.each(locale_opts, function(i,k){ - if (k in d) - out[k] = d[k]; - }); - return out; - } - - var old = $.fn.datepicker; - var datepickerPlugin = function(option){ - var args = Array.apply(null, arguments); - args.shift(); - var internal_return; - this.each(function(){ - var $this = $(this), - data = $this.data('datepicker'), - options = typeof option === 'object' && option; - if (!data){ - var elopts = opts_from_el(this, 'date'), - // Preliminary otions - xopts = $.extend({}, defaults, elopts, options), - locopts = opts_from_locale(xopts.language), - // Options priority: js args, data-attrs, locales, defaults - opts = $.extend({}, defaults, locopts, elopts, options); - if ($this.hasClass('input-daterange') || opts.inputs){ - var ropts = { - inputs: opts.inputs || $this.find('input').toArray() - }; - $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts)))); - } - else { - $this.data('datepicker', (data = new Datepicker(this, opts))); - } - } - if (typeof option === 'string' && typeof data[option] === 'function'){ - internal_return = data[option].apply(data, args); - if (internal_return !== undefined) - return false; - } - }); - if (internal_return !== undefined) - return internal_return; - else - return this; - }; - $.fn.datepicker = datepickerPlugin; - - var defaults = $.fn.datepicker.defaults = { - autoclose: false, - beforeShowDay: $.noop, - beforeShowMonth: $.noop, - calendarWeeks: false, - clearBtn: false, - toggleActive: false, - daysOfWeekDisabled: [], - datesDisabled: [], - endDate: Infinity, - forceParse: true, - format: 'mm/dd/yyyy', - keyboardNavigation: true, - language: 'en', - minViewMode: 0, - multidate: false, - multidateSeparator: ',', - orientation: "auto", - rtl: false, - startDate: -Infinity, - startView: 0, - todayBtn: false, - todayHighlight: false, - weekStart: 0, - disableTouchKeyboard: false, - enableOnReadonly: true, - container: 'body' - }; - var locale_opts = $.fn.datepicker.locale_opts = [ - 'format', - 'rtl', - 'weekStart' - ]; - $.fn.datepicker.Constructor = Datepicker; - var dates = $.fn.datepicker.dates = { - en: { - days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], - daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], - daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], - months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], - monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], - today: "Today", - clear: "Clear" - } - }; - - var DPGlobal = { - modes: [ - { - clsName: 'days', - navFnc: 'Month', - navStep: 1 - }, - { - clsName: 'months', - navFnc: 'FullYear', - navStep: 1 - }, - { - clsName: 'years', - navFnc: 'FullYear', - navStep: 10 - }], - isLeapYear: function(year){ - return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); - }, - getDaysInMonth: function(year, month){ - return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; - }, - validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, - nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g, - parseFormat: function(format){ - // IE treats \0 as a string end in inputs (truncating the value), - // so it's a bad format delimiter, anyway - var separators = format.replace(this.validParts, '\0').split('\0'), - parts = format.match(this.validParts); - if (!separators || !separators.length || !parts || parts.length === 0){ - throw new Error("Invalid date format."); - } - return {separators: separators, parts: parts}; - }, - parseDate: function(date, format, language){ - if (!date) - return undefined; - if (date instanceof Date) - return date; - if (typeof format === 'string') - format = DPGlobal.parseFormat(format); - var part_re = /([\-+]\d+)([dmwy])/, - parts = date.match(/([\-+]\d+)([dmwy])/g), - part, dir, i; - if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)){ - date = new Date(); - for (i=0; i < parts.length; i++){ - part = part_re.exec(parts[i]); - dir = parseInt(part[1]); - switch (part[2]){ - case 'd': - date.setUTCDate(date.getUTCDate() + dir); - break; - case 'm': - date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir); - break; - case 'w': - date.setUTCDate(date.getUTCDate() + dir * 7); - break; - case 'y': - date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir); - break; - } - } - return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0); - } - parts = date && date.match(this.nonpunctuation) || []; - date = new Date(); - var parsed = {}, - setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'], - setters_map = { - yyyy: function(d,v){ - return d.setUTCFullYear(v); - }, - yy: function(d,v){ - return d.setUTCFullYear(2000+v); - }, - m: function(d,v){ - if (isNaN(d)) - return d; - v -= 1; - while (v < 0) v += 12; - v %= 12; - d.setUTCMonth(v); - while (d.getUTCMonth() !== v) - d.setUTCDate(d.getUTCDate()-1); - return d; - }, - d: function(d,v){ - return d.setUTCDate(v); - } - }, - val, filtered; - setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m']; - setters_map['dd'] = setters_map['d']; - date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); - var fparts = format.parts.slice(); - // Remove noop parts - if (parts.length !== fparts.length){ - fparts = $(fparts).filter(function(i,p){ - return $.inArray(p, setters_order) !== -1; - }).toArray(); - } - // Process remainder - function match_part(){ - var m = this.slice(0, parts[i].length), - p = parts[i].slice(0, m.length); - return m.toLowerCase() === p.toLowerCase(); - } - if (parts.length === fparts.length){ - var cnt; - for (i=0, cnt = fparts.length; i < cnt; i++){ - val = parseInt(parts[i], 10); - part = fparts[i]; - if (isNaN(val)){ - switch (part){ - case 'MM': - filtered = $(dates[language].months).filter(match_part); - val = $.inArray(filtered[0], dates[language].months) + 1; - break; - case 'M': - filtered = $(dates[language].monthsShort).filter(match_part); - val = $.inArray(filtered[0], dates[language].monthsShort) + 1; - break; - } - } - parsed[part] = val; - } - var _date, s; - for (i=0; i < setters_order.length; i++){ - s = setters_order[i]; - if (s in parsed && !isNaN(parsed[s])){ - _date = new Date(date); - setters_map[s](_date, parsed[s]); - if (!isNaN(_date)) - date = _date; - } - } - } - return date; - }, - formatDate: function(date, format, language){ - if (!date) - return ''; - if (typeof format === 'string') - format = DPGlobal.parseFormat(format); - var val = { - d: date.getUTCDate(), - D: dates[language].daysShort[date.getUTCDay()], - DD: dates[language].days[date.getUTCDay()], - m: date.getUTCMonth() + 1, - M: dates[language].monthsShort[date.getUTCMonth()], - MM: dates[language].months[date.getUTCMonth()], - yy: date.getUTCFullYear().toString().substring(2), - yyyy: date.getUTCFullYear() - }; - val.dd = (val.d < 10 ? '0' : '') + val.d; - val.mm = (val.m < 10 ? '0' : '') + val.m; - date = []; - var seps = $.extend([], format.separators); - for (var i=0, cnt = format.parts.length; i <= cnt; i++){ - if (seps.length) - date.push(seps.shift()); - date.push(val[format.parts[i]]); - } - return date.join(''); - }, - headTemplate: ''+ - ''+ - '«'+ - ''+ - '»'+ - ''+ - '', - contTemplate: '', - footTemplate: ''+ - ''+ - ''+ - ''+ - ''+ - ''+ - ''+ - '' - }; - DPGlobal.template = '
'+ - '
'+ - ''+ - DPGlobal.headTemplate+ - ''+ - DPGlobal.footTemplate+ - '
'+ - '
'+ - '
'+ - ''+ - DPGlobal.headTemplate+ - DPGlobal.contTemplate+ - DPGlobal.footTemplate+ - '
'+ - '
'+ - '
'+ - ''+ - DPGlobal.headTemplate+ - DPGlobal.contTemplate+ - DPGlobal.footTemplate+ - '
'+ - '
'+ - '
'; - - $.fn.datepicker.DPGlobal = DPGlobal; - - - /* DATEPICKER NO CONFLICT - * =================== */ - - $.fn.datepicker.noConflict = function(){ - $.fn.datepicker = old; - return this; - }; - - /* DATEPICKER VERSION - * =================== */ - $.fn.datepicker.version = "1.4.0"; - - /* DATEPICKER DATA-API - * ================== */ - - $(document).on( - 'focus.datepicker.data-api click.datepicker.data-api', - '[data-provide="datepicker"]', - function(e){ - var $this = $(this); - if ($this.data('datepicker')) - return; - e.preventDefault(); - // component click requires us to explicitly show it - datepickerPlugin.call($this, 'show'); - } - ); - $(function(){ - datepickerPlugin.call($('[data-provide="datepicker-inline"]')); - }); - -}(window.jQuery)); - + */ +!function(a,b){function c(){return new Date(Date.UTC.apply(Date,arguments))}function d(){var a=new Date;return c(a.getFullYear(),a.getMonth(),a.getDate())}function e(a,b){return a.getUTCFullYear()===b.getUTCFullYear()&&a.getUTCMonth()===b.getUTCMonth()&&a.getUTCDate()===b.getUTCDate()}function f(a){return function(){return this[a].apply(this,arguments)}}function g(b,c){function d(a,b){return b.toLowerCase()}var e,f=a(b).data(),g={},h=new RegExp("^"+c.toLowerCase()+"([A-Z])");c=new RegExp("^"+c.toLowerCase());for(var i in f)c.test(i)&&(e=i.replace(h,d),g[e]=f[i]);return g}function h(b){var c={};if(p[b]||(b=b.split("-")[0],p[b])){var d=p[b];return a.each(o,function(a,b){b in d&&(c[b]=d[b])}),c}}var i=function(){var b={get:function(a){return this.slice(a)[0]},contains:function(a){for(var b=a&&a.valueOf(),c=0,d=this.length;d>c;c++)if(this[c].valueOf()===b)return c;return-1},remove:function(a){this.splice(a,1)},replace:function(b){b&&(a.isArray(b)||(b=[b]),this.clear(),this.push.apply(this,b))},clear:function(){this.length=0},copy:function(){var a=new i;return a.replace(this),a}};return function(){var c=[];return c.push.apply(c,arguments),a.extend(c,b),c}}(),j=function(b,c){this._process_options(c),this.dates=new i,this.viewDate=this.o.defaultViewDate,this.focusDate=null,this.element=a(b),this.isInline=!1,this.isInput=this.element.is("input"),this.component=this.element.hasClass("date")?this.element.find(".add-on, .input-group-addon, .btn"):!1,this.hasInput=this.component&&this.element.find("input").length,this.component&&0===this.component.length&&(this.component=!1),this.picker=a(q.template),this._buildEvents(),this._attachEvents(),this.isInline?this.picker.addClass("datepicker-inline").appendTo(this.element):this.picker.addClass("datepicker-dropdown dropdown-menu"),this.o.rtl&&this.picker.addClass("datepicker-rtl"),this.viewMode=this.o.startView,this.o.calendarWeeks&&this.picker.find("tfoot .today, tfoot .clear").attr("colspan",function(a,b){return parseInt(b)+1}),this._allow_update=!1,this.setStartDate(this._o.startDate),this.setEndDate(this._o.endDate),this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled),this.setDatesDisabled(this.o.datesDisabled),this.fillDow(),this.fillMonths(),this._allow_update=!0,this.update(),this.showMode(),this.isInline&&this.show()};j.prototype={constructor:j,_process_options:function(e){this._o=a.extend({},this._o,e);var f=this.o=a.extend({},this._o),g=f.language;switch(p[g]||(g=g.split("-")[0],p[g]||(g=n.language)),f.language=g,f.startView){case 2:case"decade":f.startView=2;break;case 1:case"year":f.startView=1;break;default:f.startView=0}switch(f.minViewMode){case 1:case"months":f.minViewMode=1;break;case 2:case"years":f.minViewMode=2;break;default:f.minViewMode=0}f.startView=Math.max(f.startView,f.minViewMode),f.multidate!==!0&&(f.multidate=Number(f.multidate)||!1,f.multidate!==!1&&(f.multidate=Math.max(0,f.multidate))),f.multidateSeparator=String(f.multidateSeparator),f.weekStart%=7,f.weekEnd=(f.weekStart+6)%7;var h=q.parseFormat(f.format);if(f.startDate!==-1/0&&(f.startDate=f.startDate?f.startDate instanceof Date?this._local_to_utc(this._zero_time(f.startDate)):q.parseDate(f.startDate,h,f.language):-1/0),1/0!==f.endDate&&(f.endDate=f.endDate?f.endDate instanceof Date?this._local_to_utc(this._zero_time(f.endDate)):q.parseDate(f.endDate,h,f.language):1/0),f.daysOfWeekDisabled=f.daysOfWeekDisabled||[],a.isArray(f.daysOfWeekDisabled)||(f.daysOfWeekDisabled=f.daysOfWeekDisabled.split(/[,\s]*/)),f.daysOfWeekDisabled=a.map(f.daysOfWeekDisabled,function(a){return parseInt(a,10)}),f.datesDisabled=f.datesDisabled||[],!a.isArray(f.datesDisabled)){var i=[];i.push(q.parseDate(f.datesDisabled,h,f.language)),f.datesDisabled=i}f.datesDisabled=a.map(f.datesDisabled,function(a){return q.parseDate(a,h,f.language)});var j=String(f.orientation).toLowerCase().split(/\s+/g),k=f.orientation.toLowerCase();if(j=a.grep(j,function(a){return/^auto|left|right|top|bottom$/.test(a)}),f.orientation={x:"auto",y:"auto"},k&&"auto"!==k)if(1===j.length)switch(j[0]){case"top":case"bottom":f.orientation.y=j[0];break;case"left":case"right":f.orientation.x=j[0]}else k=a.grep(j,function(a){return/^left|right$/.test(a)}),f.orientation.x=k[0]||"auto",k=a.grep(j,function(a){return/^top|bottom$/.test(a)}),f.orientation.y=k[0]||"auto";else;if(f.defaultViewDate){var l=f.defaultViewDate.year||(new Date).getFullYear(),m=f.defaultViewDate.month||0,o=f.defaultViewDate.day||1;f.defaultViewDate=c(l,m,o)}else f.defaultViewDate=d();f.showOnFocus=f.showOnFocus!==b?f.showOnFocus:!0},_events:[],_secondaryEvents:[],_applyEvents:function(a){for(var c,d,e,f=0;fe?(this.picker.addClass("datepicker-orient-right"),n=k.left+m-b):this.picker.addClass("datepicker-orient-left");var p,q,r=this.o.orientation.y;if("auto"===r&&(p=-g+o-c,q=g+f-(o+l+c),r=Math.max(p,q)===q?"top":"bottom"),this.picker.addClass("datepicker-orient-"+r),"top"===r?o+=l:o-=c+parseInt(this.picker.css("padding-top")),this.o.rtl){var s=e-(n+m);this.picker.css({top:o,right:s,zIndex:j})}else this.picker.css({top:o,left:n,zIndex:j});return this},_allow_update:!0,update:function(){if(!this._allow_update)return this;var b=this.dates.copy(),c=[],d=!1;return arguments.length?(a.each(arguments,a.proxy(function(a,b){b instanceof Date&&(b=this._local_to_utc(b)),c.push(b)},this)),d=!0):(c=this.isInput?this.element.val():this.element.data("date")||this.element.find("input").val(),c=c&&this.o.multidate?c.split(this.o.multidateSeparator):[c],delete this.element.data().date),c=a.map(c,a.proxy(function(a){return q.parseDate(a,this.o.format,this.o.language)},this)),c=a.grep(c,a.proxy(function(a){return athis.o.endDate||!a},this),!0),this.dates.replace(c),this.dates.length?this.viewDate=new Date(this.dates.get(-1)):this.viewDatethis.o.endDate&&(this.viewDate=new Date(this.o.endDate)),d?this.setValue():c.length&&String(b)!==String(this.dates)&&this._trigger("changeDate"),!this.dates.length&&b.length&&this._trigger("clearDate"),this.fill(),this},fillDow:function(){var a=this.o.weekStart,b="";if(this.o.calendarWeeks){this.picker.find(".datepicker-days thead tr:first-child .datepicker-switch").attr("colspan",function(a,b){return parseInt(b)+1});var c=' ';b+=c}for(;a'+p[this.o.language].daysMin[a++%7]+"";b+="",this.picker.find(".datepicker-days thead").append(b)},fillMonths:function(){for(var a="",b=0;12>b;)a+=''+p[this.o.language].monthsShort[b++]+"";this.picker.find(".datepicker-months td").html(a)},setRange:function(b){b&&b.length?this.range=a.map(b,function(a){return a.valueOf()}):delete this.range,this.fill()},getClassNames:function(b){var c=[],d=this.viewDate.getUTCFullYear(),f=this.viewDate.getUTCMonth(),g=new Date;return b.getUTCFullYear()d||b.getUTCFullYear()===d&&b.getUTCMonth()>f)&&c.push("new"),this.focusDate&&b.valueOf()===this.focusDate.valueOf()&&c.push("focused"),this.o.todayHighlight&&b.getUTCFullYear()===g.getFullYear()&&b.getUTCMonth()===g.getMonth()&&b.getUTCDate()===g.getDate()&&c.push("today"),-1!==this.dates.contains(b)&&c.push("active"),(b.valueOf()this.o.endDate||-1!==a.inArray(b.getUTCDay(),this.o.daysOfWeekDisabled))&&c.push("disabled"),this.o.datesDisabled.length>0&&a.grep(this.o.datesDisabled,function(a){return e(b,a)}).length>0&&c.push("disabled","disabled-date"),this.range&&(b>this.range[0]&&b"),this.o.calendarWeeks)){var u=new Date(+n+(this.o.weekStart-n.getUTCDay()-7)%7*864e5),v=new Date(Number(u)+(11-u.getUTCDay())%7*864e5),w=new Date(Number(w=c(v.getUTCFullYear(),0,1))+(11-w.getUTCDay())%7*864e5),x=(v-w)/864e5/7+1;t.push(''+x+"")}if(s=this.getClassNames(n),s.push("day"),this.o.beforeShowDay!==a.noop){var y=this.o.beforeShowDay(this._utc_to_local(n));y===b?y={}:"boolean"==typeof y?y={enabled:y}:"string"==typeof y&&(y={classes:y}),y.enabled===!1&&s.push("disabled"),y.classes&&(s=s.concat(y.classes.split(/\s+/))),y.tooltip&&(d=y.tooltip)}s=a.unique(s),t.push('"+n.getUTCDate()+""),d=null,n.getUTCDay()===this.o.weekEnd&&t.push(""),n.setUTCDate(n.getUTCDate()+1)}this.picker.find(".datepicker-days tbody").empty().append(t.join(""));var z=this.picker.find(".datepicker-months").find("th:eq(1)").text(f).end().find("span").removeClass("active");if(a.each(this.dates,function(a,b){b.getUTCFullYear()===f&&z.eq(b.getUTCMonth()).addClass("active")}),(h>f||f>j)&&z.addClass("disabled"),f===h&&z.slice(0,i).addClass("disabled"),f===j&&z.slice(k+1).addClass("disabled"),this.o.beforeShowMonth!==a.noop){var A=this;a.each(z,function(b,c){if(!a(c).hasClass("disabled")){var d=new Date(f,b,1),e=A.o.beforeShowMonth(d);e===!1&&a(c).addClass("disabled")}})}t="",f=10*parseInt(f/10,10);var B=this.picker.find(".datepicker-years").find("th:eq(1)").text(f+"-"+(f+9)).end().find("td");f-=1;for(var C,D=a.map(this.dates,function(a){return a.getUTCFullYear()}),E=-1;11>E;E++)C=["year"],-1===E?C.push("old"):10===E&&C.push("new"),-1!==a.inArray(f,D)&&C.push("active"),(h>f||f>j)&&C.push("disabled"),t+=''+f+"",f+=1;B.html(t)}},updateNavArrows:function(){if(this._allow_update){var a=new Date(this.viewDate),b=a.getUTCFullYear(),c=a.getUTCMonth();switch(this.viewMode){case 0:this.picker.find(".prev").css(this.o.startDate!==-1/0&&b<=this.o.startDate.getUTCFullYear()&&c<=this.o.startDate.getUTCMonth()?{visibility:"hidden"}:{visibility:"visible"}),this.picker.find(".next").css(1/0!==this.o.endDate&&b>=this.o.endDate.getUTCFullYear()&&c>=this.o.endDate.getUTCMonth()?{visibility:"hidden"}:{visibility:"visible"});break;case 1:case 2:this.picker.find(".prev").css(this.o.startDate!==-1/0&&b<=this.o.startDate.getUTCFullYear()?{visibility:"hidden"}:{visibility:"visible"}),this.picker.find(".next").css(1/0!==this.o.endDate&&b>=this.o.endDate.getUTCFullYear()?{visibility:"hidden"}:{visibility:"visible"})}}},click:function(b){b.preventDefault();var d,e,f,g=a(b.target).closest("span, td, th");if(1===g.length)switch(g[0].nodeName.toLowerCase()){case"th":switch(g[0].className){case"datepicker-switch":this.showMode(1);break;case"prev":case"next":var h=q.modes[this.viewMode].navStep*("prev"===g[0].className?-1:1);switch(this.viewMode){case 0:this.viewDate=this.moveMonth(this.viewDate,h),this._trigger("changeMonth",this.viewDate);break;case 1:case 2:this.viewDate=this.moveYear(this.viewDate,h),1===this.viewMode&&this._trigger("changeYear",this.viewDate)}this.fill();break;case"today":var i=new Date;i=c(i.getFullYear(),i.getMonth(),i.getDate(),0,0,0),this.showMode(-2);var j="linked"===this.o.todayBtn?null:"view";this._setDate(i,j);break;case"clear":this.clearDates()}break;case"span":g.hasClass("disabled")||(this.viewDate.setUTCDate(1),g.hasClass("month")?(f=1,e=g.parent().find("span").index(g),d=this.viewDate.getUTCFullYear(),this.viewDate.setUTCMonth(e),this._trigger("changeMonth",this.viewDate),1===this.o.minViewMode&&this._setDate(c(d,e,f))):(f=1,e=0,d=parseInt(g.text(),10)||0,this.viewDate.setUTCFullYear(d),this._trigger("changeYear",this.viewDate),2===this.o.minViewMode&&this._setDate(c(d,e,f))),this.showMode(-1),this.fill());break;case"td":g.hasClass("day")&&!g.hasClass("disabled")&&(f=parseInt(g.text(),10)||1,d=this.viewDate.getUTCFullYear(),e=this.viewDate.getUTCMonth(),g.hasClass("old")?0===e?(e=11,d-=1):e-=1:g.hasClass("new")&&(11===e?(e=0,d+=1):e+=1),this._setDate(c(d,e,f)))}this.picker.is(":visible")&&this._focused_from&&a(this._focused_from).focus(),delete this._focused_from},_toggle_multidate:function(a){var b=this.dates.contains(a);if(a||this.dates.clear(),-1!==b?(this.o.multidate===!0||this.o.multidate>1||this.o.toggleActive)&&this.dates.remove(b):this.o.multidate===!1?(this.dates.clear(),this.dates.push(a)):this.dates.push(a),"number"==typeof this.o.multidate)for(;this.dates.length>this.o.multidate;)this.dates.remove(0)},_setDate:function(a,b){b&&"date"!==b||this._toggle_multidate(a&&new Date(a)),b&&"view"!==b||(this.viewDate=a&&new Date(a)),this.fill(),this.setValue(),b&&"view"===b||this._trigger("changeDate");var c;this.isInput?c=this.element:this.component&&(c=this.element.find("input")),c&&c.change(),!this.o.autoclose||b&&"date"!==b||this.hide()},moveMonth:function(a,c){if(!a)return b;if(!c)return a;var d,e,f=new Date(a.valueOf()),g=f.getUTCDate(),h=f.getUTCMonth(),i=Math.abs(c);if(c=c>0?1:-1,1===i)e=-1===c?function(){return f.getUTCMonth()===h}:function(){return f.getUTCMonth()!==d},d=h+c,f.setUTCMonth(d),(0>d||d>11)&&(d=(d+12)%12);else{for(var j=0;i>j;j++)f=this.moveMonth(f,c);d=f.getUTCMonth(),f.setUTCDate(g),e=function(){return d!==f.getUTCMonth()}}for(;e();)f.setUTCDate(--g),f.setUTCMonth(d);return f},moveYear:function(a,b){return this.moveMonth(a,12*b)},dateWithinRange:function(a){return a>=this.o.startDate&&a<=this.o.endDate},keydown:function(a){if(!this.picker.is(":visible"))return void(27===a.keyCode&&this.show());var b,c,e,f=!1,g=this.focusDate||this.viewDate;switch(a.keyCode){case 27:this.focusDate?(this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill()):this.hide(),a.preventDefault();break;case 37:case 39:if(!this.o.keyboardNavigation)break;b=37===a.keyCode?-1:1,a.ctrlKey?(c=this.moveYear(this.dates.get(-1)||d(),b),e=this.moveYear(g,b),this._trigger("changeYear",this.viewDate)):a.shiftKey?(c=this.moveMonth(this.dates.get(-1)||d(),b),e=this.moveMonth(g,b),this._trigger("changeMonth",this.viewDate)):(c=new Date(this.dates.get(-1)||d()),c.setUTCDate(c.getUTCDate()+b),e=new Date(g),e.setUTCDate(g.getUTCDate()+b)),this.dateWithinRange(e)&&(this.focusDate=this.viewDate=e,this.setValue(),this.fill(),a.preventDefault());break;case 38:case 40:if(!this.o.keyboardNavigation)break;b=38===a.keyCode?-1:1,a.ctrlKey?(c=this.moveYear(this.dates.get(-1)||d(),b),e=this.moveYear(g,b),this._trigger("changeYear",this.viewDate)):a.shiftKey?(c=this.moveMonth(this.dates.get(-1)||d(),b),e=this.moveMonth(g,b),this._trigger("changeMonth",this.viewDate)):(c=new Date(this.dates.get(-1)||d()),c.setUTCDate(c.getUTCDate()+7*b),e=new Date(g),e.setUTCDate(g.getUTCDate()+7*b)),this.dateWithinRange(e)&&(this.focusDate=this.viewDate=e,this.setValue(),this.fill(),a.preventDefault());break;case 32:break;case 13:g=this.focusDate||this.dates.get(-1)||this.viewDate,this.o.keyboardNavigation&&(this._toggle_multidate(g),f=!0),this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.setValue(),this.fill(),this.picker.is(":visible")&&(a.preventDefault(),"function"==typeof a.stopPropagation?a.stopPropagation():a.cancelBubble=!0,this.o.autoclose&&this.hide());break;case 9:this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill(),this.hide()}if(f){this._trigger(this.dates.length?"changeDate":"clearDate");var h;this.isInput?h=this.element:this.component&&(h=this.element.find("input")),h&&h.change()}},showMode:function(a){a&&(this.viewMode=Math.max(this.o.minViewMode,Math.min(2,this.viewMode+a))),this.picker.children("div").hide().filter(".datepicker-"+q.modes[this.viewMode].clsName).css("display","block"),this.updateNavArrows()}};var k=function(b,c){this.element=a(b),this.inputs=a.map(c.inputs,function(a){return a.jquery?a[0]:a}),delete c.inputs,m.call(a(this.inputs),c).bind("changeDate",a.proxy(this.dateUpdated,this)),this.pickers=a.map(this.inputs,function(b){return a(b).data("datepicker")}),this.updateDates()};k.prototype={updateDates:function(){this.dates=a.map(this.pickers,function(a){return a.getUTCDate()}),this.updateRanges()},updateRanges:function(){var b=a.map(this.dates,function(a){return a.valueOf()});a.each(this.pickers,function(a,c){c.setRange(b)})},dateUpdated:function(b){if(!this.updating){this.updating=!0;var c=a(b.target).data("datepicker"),d=c.getUTCDate(),e=a.inArray(b.target,this.inputs),f=e-1,g=e+1,h=this.inputs.length;if(-1!==e){if(a.each(this.pickers,function(a,b){b.getUTCDate()||b.setUTCDate(d)}),d=0&&dthis.dates[g])for(;h>g&&d>this.dates[g];)this.pickers[g++].setUTCDate(d);this.updateDates(),delete this.updating}}},remove:function(){a.map(this.pickers,function(a){a.remove()}),delete this.element.data().datepicker}};var l=a.fn.datepicker,m=function(c){var d=Array.apply(null,arguments);d.shift();var e;return this.each(function(){var f=a(this),i=f.data("datepicker"),l="object"==typeof c&&c;if(!i){var m=g(this,"date"),o=a.extend({},n,m,l),p=h(o.language),q=a.extend({},n,p,m,l);if(f.hasClass("input-daterange")||q.inputs){var r={inputs:q.inputs||f.find("input").toArray()};f.data("datepicker",i=new k(this,a.extend(q,r)))}else f.data("datepicker",i=new j(this,q))}return"string"==typeof c&&"function"==typeof i[c]&&(e=i[c].apply(i,d),e!==b)?!1:void 0}),e!==b?e:this};a.fn.datepicker=m;var n=a.fn.datepicker.defaults={autoclose:!1,beforeShowDay:a.noop,beforeShowMonth:a.noop,calendarWeeks:!1,clearBtn:!1,toggleActive:!1,daysOfWeekDisabled:[],datesDisabled:[],endDate:1/0,forceParse:!0,format:"mm/dd/yyyy",keyboardNavigation:!0,language:"en",minViewMode:0,multidate:!1,multidateSeparator:",",orientation:"auto",rtl:!1,startDate:-1/0,startView:0,todayBtn:!1,todayHighlight:!1,weekStart:0,disableTouchKeyboard:!1,enableOnReadonly:!0,container:"body"},o=a.fn.datepicker.locale_opts=["format","rtl","weekStart"];a.fn.datepicker.Constructor=j;var p=a.fn.datepicker.dates={en:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa","Su"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",clear:"Clear"}},q={modes:[{clsName:"days",navFnc:"Month",navStep:1},{clsName:"months",navFnc:"FullYear",navStep:1},{clsName:"years",navFnc:"FullYear",navStep:10}],isLeapYear:function(a){return a%4===0&&a%100!==0||a%400===0},getDaysInMonth:function(a,b){return[31,q.isLeapYear(a)?29:28,31,30,31,30,31,31,30,31,30,31][b]},validParts:/dd?|DD?|mm?|MM?|yy(?:yy)?/g,nonpunctuation:/[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,parseFormat:function(a){var b=a.replace(this.validParts,"\x00").split("\x00"),c=a.match(this.validParts);if(!b||!b.length||!c||0===c.length)throw new Error("Invalid date format.");return{separators:b,parts:c}},parseDate:function(d,e,f){function g(){var a=this.slice(0,m[k].length),b=m[k].slice(0,a.length);return a.toLowerCase()===b.toLowerCase()}if(!d)return b;if(d instanceof Date)return d;"string"==typeof e&&(e=q.parseFormat(e));var h,i,k,l=/([\-+]\d+)([dmwy])/,m=d.match(/([\-+]\d+)([dmwy])/g);if(/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(d)){for(d=new Date,k=0;kb;)b+=12;for(b%=12,a.setUTCMonth(b);a.getUTCMonth()!==b;)a.setUTCDate(a.getUTCDate()-1);return a},d:function(a,b){return a.setUTCDate(b)}};t.M=t.MM=t.mm=t.m,t.dd=t.d,d=c(d.getFullYear(),d.getMonth(),d.getDate(),0,0,0);var u=e.parts.slice();if(m.length!==u.length&&(u=a(u).filter(function(b,c){return-1!==a.inArray(c,s)}).toArray()),m.length===u.length){var v;for(k=0,v=u.length;v>k;k++){if(n=parseInt(m[k],10),h=u[k],isNaN(n))switch(h){case"MM":o=a(p[f].months).filter(g),n=a.inArray(o[0],p[f].months)+1;break;case"M":o=a(p[f].monthsShort).filter(g),n=a.inArray(o[0],p[f].monthsShort)+1}r[h]=n}var w,x;for(k=0;k=g;g++)f.length&&b.push(f.shift()),b.push(e[c.parts[g]]);return b.join("")},headTemplate:'«»',contTemplate:'',footTemplate:''};q.template='
'+q.headTemplate+""+q.footTemplate+'
'+q.headTemplate+q.contTemplate+q.footTemplate+'
'+q.headTemplate+q.contTemplate+q.footTemplate+"
",a.fn.datepicker.DPGlobal=q,a.fn.datepicker.noConflict=function(){return a.fn.datepicker=l,this},a.fn.datepicker.version="1.4.0",a(document).on("focus.datepicker.data-api click.datepicker.data-api",'[data-provide="datepicker"]',function(b){var c=a(this);c.data("datepicker")||(b.preventDefault(),m.call(c,"show"))}),a(function(){m.call(a('[data-provide="datepicker-inline"]'))})}(window.jQuery); +!function(a){a.fn.datepicker.dates.de={days:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag","Sonntag"],daysShort:["Son","Mon","Die","Mit","Don","Fre","Sam","Son"],daysMin:["So","Mo","Di","Mi","Do","Fr","Sa","So"],months:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthsShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],today:"Heute",clear:"Löschen",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); +!function(a){a.fn.datepicker.dates.da={days:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag","Søndag"],daysShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør","Søn"],daysMin:["Sø","Ma","Ti","On","To","Fr","Lø","Sø"],months:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],monthsShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],today:"I Dag",clear:"Nulstil"}}(jQuery); +!function(a){a.fn.datepicker.dates["pt-BR"]={days:["Domingo","Segunda","Terça","Quarta","Quinta","Sexta","Sábado","Domingo"],daysShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb","Dom"],daysMin:["Do","Se","Te","Qu","Qu","Se","Sa","Do"],months:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthsShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],today:"Hoje",clear:"Limpar"}}(jQuery); +!function(a){a.fn.datepicker.dates.nl={days:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag","zondag"],daysShort:["zo","ma","di","wo","do","vr","za","zo"],daysMin:["zo","ma","di","wo","do","vr","za","zo"],months:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthsShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],today:"Vandaag",clear:"Wissen",weekStart:1,format:"dd-mm-yyyy"}}(jQuery); +!function(a){a.fn.datepicker.dates.fr={days:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi","dimanche"],daysShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam.","dim."],daysMin:["d","l","ma","me","j","v","s","d"],months:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthsShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],today:"Aujourd'hui",clear:"Effacer",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); +!function(a){a.fn.datepicker.dates.it={days:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato","Domenica"],daysShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab","Dom"],daysMin:["Do","Lu","Ma","Me","Gi","Ve","Sa","Do"],months:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthsShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],today:"Oggi",clear:"Cancella",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); +!function(a){a.fn.datepicker.dates.lt={days:["Sekmadienis","Pirmadienis","Antradienis","Trečiadienis","Ketvirtadienis","Penktadienis","Šeštadienis","Sekmadienis"],daysShort:["S","Pr","A","T","K","Pn","Š","S"],daysMin:["Sk","Pr","An","Tr","Ke","Pn","Št","Sk"],months:["Sausis","Vasaris","Kovas","Balandis","Gegužė","Birželis","Liepa","Rugpjūtis","Rugsėjis","Spalis","Lapkritis","Gruodis"],monthsShort:["Sau","Vas","Kov","Bal","Geg","Bir","Lie","Rugp","Rugs","Spa","Lap","Gru"],today:"Šiandien",weekStart:1}}(jQuery); +!function(a){a.fn.datepicker.dates.no={days:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"],daysShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],daysMin:["Sø","Ma","Ti","On","To","Fr","Lø"],months:["Januar","Februar","Mars","April","Mai","Juni","Juli","August","September","Oktober","November","Desember"],monthsShort:["Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Des"],today:"I dag",clear:"Nullstill",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); +!function(a){a.fn.datepicker.dates.es={days:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado","Domingo"],daysShort:["Dom","Lun","Mar","Mié","Jue","Vie","Sáb","Dom"],daysMin:["Do","Lu","Ma","Mi","Ju","Vi","Sa","Do"],months:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],monthsShort:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],today:"Hoy",clear:"Borrar",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); +!function(a){a.fn.datepicker.dates.sv={days:["Söndag","Måndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag","Söndag"],daysShort:["Sön","Mån","Tis","Ons","Tor","Fre","Lör","Sön"],daysMin:["Sö","Må","Ti","On","To","Fr","Lö","Sö"],months:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],monthsShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],today:"Idag",format:"yyyy-mm-dd",weekStart:1,clear:"Rensa"}}(jQuery); /*! * typeahead.js 0.9.3 * https://github.com/twitter/typeahead diff --git a/resources/views/master.blade.php b/resources/views/master.blade.php index 79de433a6ba8..59a486405da7 100644 --- a/resources/views/master.blade.php +++ b/resources/views/master.blade.php @@ -53,6 +53,10 @@ 'sSearch': '' } } ); + + $.extend( true, $.fn.datepicker.defaults, { + language:'{{App::getLocale()}}' + }); From b9c00a0531ad355ca98a95b7928ec9bb9b402990 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 12 Jul 2015 22:43:45 +0300 Subject: [PATCH 08/62] Re-worked editing task details --- app/Http/Controllers/AccountController.php | 3 + .../Controllers/AccountGatewayController.php | 4 +- app/Http/Controllers/InvoiceController.php | 7 +- app/Http/Controllers/PaymentController.php | 77 ++-- app/Http/Controllers/TaskController.php | 58 +-- app/Http/routes.php | 1 + app/Models/Account.php | 11 +- app/Models/Invoice.php | 7 +- app/Models/Task.php | 62 ++- app/Ninja/Mailers/Mailer.php | 12 +- app/Ninja/Repositories/InvoiceRepository.php | 12 + app/Ninja/Repositories/TaskRepository.php | 24 +- .../2015_07_08_114333_simplify_tasks.php | 73 ++++ public/css/built.css | 2 + public/css/style.css | 2 + resources/lang/da/texts.php | 18 + resources/lang/de/texts.php | 19 + resources/lang/en/texts.php | 23 +- resources/lang/es/texts.php | 19 + resources/lang/es_ES/texts.php | 19 + resources/lang/fr/texts.php | 19 + resources/lang/fr_CA/texts.php | 19 + resources/lang/it/texts.php | 19 + resources/lang/lt/texts.php | 19 + resources/lang/nb_NO/texts.php | 19 + resources/lang/nl/texts.php | 19 + resources/lang/pt_BR/texts.php | 19 + resources/lang/sv/texts.php | 19 + .../views/accounts/account_gateway.blade.php | 32 +- resources/views/accounts/details.blade.php | 7 + .../views/accounts/invoice_settings.blade.php | 6 +- resources/views/header.blade.php | 30 +- resources/views/invoices/edit.blade.php | 37 +- resources/views/payments/payment.blade.php | 2 + resources/views/tasks/edit.blade.php | 374 ++++++++++-------- 35 files changed, 804 insertions(+), 289 deletions(-) create mode 100644 database/migrations/2015_07_08_114333_simplify_tasks.php diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 540567d9b339..dcb9438c1a8c 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -647,6 +647,9 @@ class AccountController extends BaseController $user->username = trim(Input::get('email')); $user->email = trim(strtolower(Input::get('email'))); $user->phone = trim(Input::get('phone')); + if (Utils::isNinja()) { + $user->dark_mode = Input::get('dark_mode') ? true : false; + } $user->save(); } diff --git a/app/Http/Controllers/AccountGatewayController.php b/app/Http/Controllers/AccountGatewayController.php index b6f3c5df22d5..ff4c84c649be 100644 --- a/app/Http/Controllers/AccountGatewayController.php +++ b/app/Http/Controllers/AccountGatewayController.php @@ -257,6 +257,8 @@ class AccountGatewayController extends BaseController } $accountGateway->accepted_credit_cards = $cardCount; + $accountGateway->show_address = Input::get('show_address') ? true : false; + $accountGateway->update_address = Input::get('update_address') ? true : false; $accountGateway->config = json_encode($config); if ($accountGatewayPublicId) { @@ -278,7 +280,7 @@ class AccountGatewayController extends BaseController Session::flash('message', $message); - return Redirect::to('company/payments'); + return Redirect::to("gateways/{$accountGateway->public_id}/edit"); } } diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 0a9220c7271d..20c938e8252f 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -375,10 +375,9 @@ class InvoiceController extends BaseController 'method' => 'POST', 'url' => 'invoices', 'title' => trans('texts.new_invoice'), - 'client' => $client, - 'tasks' => Session::get('tasks') ? json_encode(Session::get('tasks')) : null); + 'client' => $client); $data = array_merge($data, self::getViewModel()); - + return View::make('invoices.edit', $data); } @@ -417,7 +416,7 @@ class InvoiceController extends BaseController ), 'recurringHelp' => $recurringHelp, 'invoiceLabels' => Auth::user()->account->getInvoiceLabels(), - + 'tasks' => Session::get('tasks') ? json_encode(Session::get('tasks')) : null, ]; } diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index a68841d580dd..6248a8e6917b 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -233,33 +233,41 @@ class PaymentController extends BaseController private function convertInputForOmnipay($input) { - $country = Country::find($input['country_id']); - - return [ + $data = [ 'firstName' => $input['first_name'], 'lastName' => $input['last_name'], 'number' => $input['card_number'], 'expiryMonth' => $input['expiration_month'], 'expiryYear' => $input['expiration_year'], 'cvv' => $input['cvv'], - 'billingAddress1' => $input['address1'], - 'billingAddress2' => $input['address2'], - 'billingCity' => $input['city'], - 'billingState' => $input['state'], - 'billingPostcode' => $input['postal_code'], - 'billingCountry' => $country->iso_3166_2, - 'shippingAddress1' => $input['address1'], - 'shippingAddress2' => $input['address2'], - 'shippingCity' => $input['city'], - 'shippingState' => $input['state'], - 'shippingPostcode' => $input['postal_code'], - 'shippingCountry' => $country->iso_3166_2 ]; + + if (isset($input['country_id'])) { + $country = Country::find($input['country_id']); + + $data = array_merge($data, [ + 'billingAddress1' => $input['address1'], + 'billingAddress2' => $input['address2'], + 'billingCity' => $input['city'], + 'billingState' => $input['state'], + 'billingPostcode' => $input['postal_code'], + 'billingCountry' => $country->iso_3166_2, + 'shippingAddress1' => $input['address1'], + 'shippingAddress2' => $input['address2'], + 'shippingCity' => $input['city'], + 'shippingState' => $input['state'], + 'shippingPostcode' => $input['postal_code'], + 'shippingCountry' => $country->iso_3166_2 + ]); + } + + return $data; } private function getPaymentDetails($invitation, $input = null) { $invoice = $invitation->invoice; + $account = $invoice->account; $key = $invoice->account_id.'-'.$invoice->invoice_number; $currencyCode = $invoice->client->currency ? $invoice->client->currency->code : ($invoice->account->currency ? $invoice->account->currency->code : 'USD'); @@ -330,6 +338,7 @@ class PaymentController extends BaseController 'currencyId' => $client->getCurrencyId(), 'account' => $client->account, 'hideLogo' => $account->isWhiteLabel(), + 'showAddress' => $accountGateway->show_address, ]; return View::make('payments.payment', $data); @@ -498,19 +507,30 @@ class PaymentController extends BaseController public function do_payment($invitationKey, $onSite = true, $useToken = false) { - $rules = array( + $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail(); + $invoice = $invitation->invoice; + $client = $invoice->client; + $account = $client->account; + $accountGateway = $account->getGatewayByType(Session::get('payment_type')); + + $rules = [ 'first_name' => 'required', 'last_name' => 'required', 'card_number' => 'required', 'expiration_month' => 'required', 'expiration_year' => 'required', 'cvv' => 'required', - 'address1' => 'required', - 'city' => 'required', - 'state' => 'required', - 'postal_code' => 'required', - 'country_id' => 'required', - ); + ]; + + if ($accountGateway->show_address) { + $rules = array_merge($rules, [ + 'address1' => 'required', + 'city' => 'required', + 'state' => 'required', + 'postal_code' => 'required', + 'country_id' => 'required', + ]); + } if ($onSite) { $validator = Validator::make(Input::all(), $rules); @@ -522,23 +542,16 @@ class PaymentController extends BaseController ->withInput(); } } - - $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail(); - $invoice = $invitation->invoice; - $client = $invoice->client; - $account = $client->account; - $accountGateway = $account->getGatewayByType(Session::get('payment_type')); - - /* - if ($onSite) { + + if ($onSite && $accountGateway->update_address) { $client->address1 = trim(Input::get('address1')); $client->address2 = trim(Input::get('address2')); $client->city = trim(Input::get('city')); $client->state = trim(Input::get('state')); $client->postal_code = trim(Input::get('postal_code')); + $client->country_id = Input::get('country_id'); $client->save(); } - */ try { $gateway = self::createGateway($accountGateway); diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index a1b12dad293c..9ca05f64f5d4 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -13,31 +13,19 @@ use DropdownButton; use App\Models\Client; use App\Models\Task; -/* -use Auth; -use Cache; - -use App\Models\Activity; -use App\Models\Contact; -use App\Models\Invoice; -use App\Models\Size; -use App\Models\PaymentTerm; -use App\Models\Industry; -use App\Models\Currency; -use App\Models\Country; -*/ - use App\Ninja\Repositories\TaskRepository; +use App\Ninja\Repositories\InvoiceRepository; class TaskController extends BaseController { protected $taskRepo; - public function __construct(TaskRepository $taskRepo) + public function __construct(TaskRepository $taskRepo, InvoiceRepository $invoiceRepo) { parent::__construct(); $this->taskRepo = $taskRepo; + $this->invoiceRepo = $invoiceRepo; } /** @@ -71,8 +59,8 @@ class TaskController extends BaseController ->addColumn('client_name', function ($model) { return $model->client_public_id ? link_to('clients/'.$model->client_public_id, Utils::getClientDisplayName($model)) : ''; }); } - return $table->addColumn('start_time', function($model) { return Utils::fromSqlDateTime($model->start_time); }) - ->addColumn('duration', function($model) { return gmdate('H:i:s', $model->is_running ? time() - strtotime($model->start_time) : $model->duration); }) + return $table->addColumn('created_at', function($model) { return Task::calcStartTime($model); }) + ->addColumn('time_log', function($model) { return gmdate('H:i:s', Task::calcDuration($model)); }) ->addColumn('description', function($model) { return $model->description; }) ->addColumn('invoice_number', function($model) { return self::getStatusLabel($model); }) ->addColumn('dropdown', function ($model) { @@ -169,8 +157,16 @@ class TaskController extends BaseController if ($task->invoice) { $actions[] = ['url' => URL::to("inovices/{$task->invoice->public_id}/edit"), 'label' => trans("texts.view_invoice")]; } else { - $actions[] = ['url' => 'javascript:submitAction("invoice")', 'label' => trans("texts.invoice_task")]; + $actions[] = ['url' => 'javascript:submitAction("invoice")', 'label' => trans("texts.create_invoice")]; + + // check for any open invoices + $invoices = $task->client_id ? $this->invoiceRepo->findOpenInvoices($task->client_id) : []; + + foreach ($invoices as $invoice) { + $actions[] = ['url' => 'javascript:submitAction("add_to_invoice", '.$invoice->public_id.')', 'label' => trans("texts.add_to_invoice", ["invoice" => $invoice->invoice_number])]; + } } + $actions[] = DropdownButton::DIVIDER; if (!$task->trashed()) { $actions[] = ['url' => 'javascript:submitAction("archive")', 'label' => trans('texts.archive_task')]; @@ -178,15 +174,15 @@ class TaskController extends BaseController } else { $actions[] = ['url' => 'javascript:submitAction("restore")', 'label' => trans('texts.restore_task')]; } - + $data = [ 'task' => $task, 'clientPublicId' => $task->client ? $task->client->public_id : 0, 'method' => 'PUT', 'url' => 'tasks/'.$publicId, 'title' => trans('texts.edit_task'), - 'duration' => $task->resume_time ? ($task->duration + strtotime('now') - strtotime($task->resume_time)) : (strtotime('now') - strtotime($task->start_time)), - 'actions' => $actions + 'duration' => $task->is_running ? $task->getCurrentDuration() : $task->getDuration(), + 'actions' => $actions, ]; $data = array_merge($data, self::getViewModel()); @@ -216,7 +212,7 @@ class TaskController extends BaseController { $action = Input::get('action'); - if (in_array($action, ['archive', 'delete', 'invoice', 'restore'])) { + if (in_array($action, ['archive', 'delete', 'invoice', 'restore', 'add_to_invoice'])) { return self::bulk(); } @@ -235,12 +231,11 @@ class TaskController extends BaseController $this->taskRepo->save($ids, ['action' => $action]); Session::flash('message', trans('texts.stopped_task')); return Redirect::to('tasks'); - } else if ($action == 'invoice') { - + } else if ($action == 'invoice' || $action == 'add_to_invoice') { $tasks = Task::scope($ids)->with('client')->get(); $clientPublicId = false; $data = []; - + foreach ($tasks as $task) { if ($task->client) { if (!$clientPublicId) { @@ -258,16 +253,21 @@ class TaskController extends BaseController Session::flash('error', trans('texts.task_error_invoiced')); return Redirect::to('tasks'); } - + $data[] = [ 'publicId' => $task->public_id, 'description' => $task->description, - 'startTime' => Utils::fromSqlDateTime($task->start_time), - 'duration' => round($task->duration / (60 * 60), 2) + 'startTime' => $task->getStartTime(), + 'duration' => $task->getHours(), ]; } - return Redirect::to("invoices/create/{$clientPublicId}")->with('tasks', $data); + if ($action == 'invoice') { + return Redirect::to("invoices/create/{$clientPublicId}")->with('tasks', $data); + } else { + $invoiceId = Input::get('invoice_id'); + return Redirect::to("invoices/{$invoiceId}/edit")->with('tasks', $data); + } } else { $count = $this->taskRepo->bulk($ids, $action); diff --git a/app/Http/routes.php b/app/Http/routes.php index 0825afb383e4..542c2638e7e8 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -1,5 +1,6 @@ share_counter ? $this->quote_number_counter : $this->invoice_number_counter; $prefix .= $isQuote ? $this->quote_number_prefix : $this->invoice_number_prefix; - + // confirm the invoice number isn't already taken do { $number = $prefix.str_pad($counter, 4, "0", STR_PAD_LEFT); @@ -186,11 +186,14 @@ class Account extends Eloquent { // check if the user modified the invoice number if (!$isRecurring && $invoiceNumber != $this->getNextInvoiceNumber($isQuote)) { - $number = intval(preg_replace('/[^0-9]/', '', $invoiceNumber)); + // remove the prefix + $prefix = $isQuote ? $this->quote_number_prefix : $this->invoice_number_prefix; + $invoiceNumber = preg_replace('/^'.$prefix.'/', '', $invoiceNumber); + $invoiceNumber = intval(preg_replace('/[^0-9]/', '', $invoiceNumber)); if ($isQuote && !$this->share_counter) { - $this->quote_number_counter = $number + 1; + $this->quote_number_counter = $invoiceNumber + 1; } else { - $this->invoice_number_counter = $number + 1; + $this->invoice_number_counter = $invoiceNumber + 1; } // otherwise, just increment the counter } else { diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 6207bfb14041..27c0999c7a2c 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -252,8 +252,11 @@ class Invoice extends EntityModel } } -Invoice::created(function ($invoice) { +Invoice::creating(function ($invoice) { $invoice->account->incrementCounter($invoice->invoice_number, $invoice->is_quote, $invoice->recurring_invoice_id); +}); + +Invoice::created(function ($invoice) { Activity::createInvoice($invoice); }); @@ -267,4 +270,4 @@ Invoice::deleting(function ($invoice) { Invoice::restoring(function ($invoice) { Activity::restoreInvoice($invoice); -}); +}); \ No newline at end of file diff --git a/app/Models/Task.php b/app/Models/Task.php index 68cfaffe2e75..4ccbf9688ab3 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -1,7 +1,7 @@ belongsTo('App\Models\Client')->withTrashed(); } + + public static function calcStartTime($task) + { + $parts = json_decode($task->time_log) ?: []; + + if (count($parts)) { + return Utils::timestampToDateTimeString($parts[0][0]); + } else { + return ''; + } + } + + public function getStartTime() + { + return self::calcStartTime($this); + } + + public static function calcDuration($task) + { + $duration = 0; + $parts = json_decode($task->time_log) ?: []; + + foreach ($parts as $part) { + if (count($part) == 1 || !$part[1]) { + $duration += time() - $part[0]; + } else { + $duration += $part[1] - $part[0]; + } + } + + return $duration; + } + + public function getDuration() + { + return self::calcDuration($this); + } + + public function getCurrentDuration() + { + $parts = json_decode($this->time_log) ?: []; + $part = $parts[count($parts)-1]; + + if (count($part) == 1 || !$part[1]) { + return time() - $part[0]; + } else { + return 0; + } + } + + public function hasPreviousDuration() + { + $parts = json_decode($this->time_log) ?: []; + return count($parts) && (count($parts[0]) && $parts[0][1]); + } + + public function getHours() + { + return round($this->getDuration() / (60 * 60), 2); + } } Task::created(function ($task) { diff --git a/app/Ninja/Mailers/Mailer.php b/app/Ninja/Mailers/Mailer.php index 47c7756ab59f..ffd0abdadee8 100644 --- a/app/Ninja/Mailers/Mailer.php +++ b/app/Ninja/Mailers/Mailer.php @@ -34,10 +34,14 @@ class Mailer }); return true; - } catch (Exception $e) { - $response = $e->getResponse()->getBody()->getContents(); - $response = json_decode($response); - return nl2br($response->Message); + } catch (Exception $exception) { + if (method_exists($exception, 'getResponse')) { + $response = $exception->getResponse()->getBody()->getContents(); + $response = json_decode($response); + return nl2br($response->Message); + } else { + return $exception->getMessage(); + } } } } diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index f4caa63ee2e2..ce0397d64b4d 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -536,4 +536,16 @@ class InvoiceRepository return count($invoices); } + + public function findOpenInvoices($clientId) + { + return Invoice::scope() + ->whereClientId($clientId) + ->whereIsQuote(false) + ->whereIsRecurring(false) + ->whereHasTasks(true) + ->where('balance', '>', 0) + ->select(['public_id', 'invoice_number']) + ->get(); + } } diff --git a/app/Ninja/Repositories/TaskRepository.php b/app/Ninja/Repositories/TaskRepository.php index feb764850ad3..4504e78fb7fa 100644 --- a/app/Ninja/Repositories/TaskRepository.php +++ b/app/Ninja/Repositories/TaskRepository.php @@ -23,7 +23,7 @@ class TaskRepository }) ->where('contacts.deleted_at', '=', null) ->where('clients.deleted_at', '=', null) - ->select('tasks.public_id', 'clients.name as client_name', 'clients.public_id as client_public_id', 'contacts.first_name', 'contacts.email', 'contacts.last_name', 'invoices.invoice_status_id', 'tasks.start_time', 'tasks.description', 'tasks.duration', 'tasks.is_deleted', 'tasks.deleted_at', 'invoices.invoice_number', 'invoices.public_id as invoice_public_id', 'tasks.is_running'); + ->select('tasks.public_id', 'clients.name as client_name', 'clients.public_id as client_public_id', 'contacts.first_name', 'contacts.email', 'contacts.last_name', 'invoices.invoice_status_id', 'tasks.description', 'tasks.is_deleted', 'tasks.deleted_at', 'invoices.invoice_number', 'invoices.public_id as invoice_public_id', 'tasks.is_running', 'tasks.time_log', 'tasks.created_at'); if ($clientPublicId) { $query->where('clients.public_id', '=', $clientPublicId); @@ -60,36 +60,20 @@ class TaskRepository $task->description = trim($data['description']); } - $timeLog = $task->time_log ? json_decode($task->time_log, true) : []; - + //$timeLog = $task->time_log ? json_decode($task->time_log, true) : []; + $timeLog = isset($data['time_log']) ? json_decode($data['time_log']) : []; if ($data['action'] == 'start') { - $task->start_time = Carbon::now()->toDateTimeString(); $task->is_running = true; $timeLog[] = [strtotime('now'), false]; } else if ($data['action'] == 'resume') { - $task->break_duration = strtotime('now') - strtotime($task->start_time) + $task->duration; - $task->resume_time = Carbon::now()->toDateTimeString(); $task->is_running = true; $timeLog[] = [strtotime('now'), false]; } else if ($data['action'] == 'stop' && $task->is_running) { - if ($task->resume_time) { - $task->duration = $task->duration + strtotime('now') - strtotime($task->resume_time); - $task->resume_time = null; - } else { - $task->duration = strtotime('now') - strtotime($task->start_time); - } - $timeLog[count($timeLog)-1][1] = strtotime('now'); + $timeLog[count($timeLog)-1][1] = time(); $task->is_running = false; - } else if ($data['action'] == 'save' && !$task->is_running) { - $task->start_time = $data['start_time']; - $task->duration = $data['duration']; - $task->break_duration = $data['break_duration']; } - $task->duration = max($task->duration, 0); - $task->break_duration = max($task->break_duration, 0); $task->time_log = json_encode($timeLog); - $task->save(); return $task; diff --git a/database/migrations/2015_07_08_114333_simplify_tasks.php b/database/migrations/2015_07_08_114333_simplify_tasks.php new file mode 100644 index 000000000000..b0136a33ec91 --- /dev/null +++ b/database/migrations/2015_07_08_114333_simplify_tasks.php @@ -0,0 +1,73 @@ +start_time); + if (!$task->time_log || !count(json_decode($task->time_log))) { + $task->time_log = json_encode([[$startTime, $startTime + $task->duration]]); + $task->save(); + } elseif ($task->getDuration() != intval($task->duration)) { + $task->time_log = json_encode([[$startTime, $startTime + $task->duration]]); + $task->save(); + } + } + + Schema::table('tasks', function($table) + { + $table->dropColumn('start_time'); + $table->dropColumn('duration'); + $table->dropColumn('break_duration'); + $table->dropColumn('resume_time'); + }); + + Schema::table('users', function($table) + { + $table->boolean('dark_mode')->default(false)->nullable(); + }); + + Schema::table('users', function($table) + { + $table->dropColumn('theme_id'); + }); + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('tasks', function($table) + { + $table->timestamp('start_time')->nullable(); + $table->integer('duration')->nullable(); + $table->timestamp('resume_time')->nullable(); + $table->integer('break_duration')->nullable(); + }); + + Schema::table('users', function($table) + { + $table->dropColumn('dark_mode'); + }); + Schema::table('users', function($table) + { + $table->integer('theme_id')->nullable(); + }); + } + +} diff --git a/public/css/built.css b/public/css/built.css index 5c4e52f5c523..ab4839c61fd8 100644 --- a/public/css/built.css +++ b/public/css/built.css @@ -2433,6 +2433,8 @@ th {border-left: 1px solid #d26b26; } .table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td { vertical-align: middle; border-top: none; +} +table.invoice-table>thead>tr>th, table.invoice-table>tbody>tr>th, table.invoice-table>tfoot>tr>th, table.invoice-table>thead>tr>td, table.invoice-table>tbody>tr>td, table.invoice-table>tfoot>tr>td { border-bottom: 1px solid #dfe0e1; } table.dataTable.no-footer { diff --git a/public/css/style.css b/public/css/style.css index 5e536810750b..6c31f3041907 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -83,6 +83,8 @@ th {border-left: 1px solid #d26b26; } .table>thead>tr>th, .table>tbody>tr>th, .table>tfoot>tr>th, .table>thead>tr>td, .table>tbody>tr>td, .table>tfoot>tr>td { vertical-align: middle; border-top: none; +} +table.invoice-table>thead>tr>th, table.invoice-table>tbody>tr>th, table.invoice-table>tfoot>tr>th, table.invoice-table>thead>tr>td, table.invoice-table>tbody>tr>td, table.invoice-table>tfoot>tr>td { border-bottom: 1px solid #dfe0e1; } table.dataTable.no-footer { diff --git a/resources/lang/da/texts.php b/resources/lang/da/texts.php index 706250b733d1..5c9b160aa6bb 100644 --- a/resources/lang/da/texts.php +++ b/resources/lang/da/texts.php @@ -706,6 +706,24 @@ return array( 'login' => 'Login', 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', diff --git a/resources/lang/de/texts.php b/resources/lang/de/texts.php index 461b87811485..482c29ef0b1c 100644 --- a/resources/lang/de/texts.php +++ b/resources/lang/de/texts.php @@ -697,5 +697,24 @@ return array( 'login' => 'Login', 'or' => 'oder', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + ); \ No newline at end of file diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 728fd8c37690..7861fb9a80ac 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -276,9 +276,9 @@ return array( // Payment page 'secure_payment' => 'Secure Payment', - 'card_number' => 'Card number', - 'expiration_month' => 'Expiration month', - 'expiration_year' => 'Expiration year', + 'card_number' => 'Card Number', + 'expiration_month' => 'Expiration Month', + 'expiration_year' => 'Expiration Year', 'cvv' => 'CVV', // Security alerts @@ -526,11 +526,11 @@ return array( 'token_billing_secure' => 'The data is stored securely by :stripe_link', 'support' => 'Support', - 'contact_information' => 'Contact information', + 'contact_information' => 'Contact Information', '256_encryption' => '256-Bit Encryption', 'amount_due' => 'Amount due', - 'billing_address' => 'Billing address', - 'billing_method' => 'Billing method', + 'billing_address' => 'Billing Address', + 'billing_method' => 'Billing Method', 'order_overview' => 'Order overview', 'match_address' => '*Address must match address associated with credit card.', 'click_once' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', @@ -711,5 +711,16 @@ return array( 'payment_terms_help' => 'Sets the default invoice due date', 'unlink_account' => 'Unlink Account', 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', ); diff --git a/resources/lang/es/texts.php b/resources/lang/es/texts.php index 0d32d825eed3..826902066b98 100644 --- a/resources/lang/es/texts.php +++ b/resources/lang/es/texts.php @@ -676,5 +676,24 @@ return array( 'login' => 'Login', 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + ); \ No newline at end of file diff --git a/resources/lang/es_ES/texts.php b/resources/lang/es_ES/texts.php index 38e48e8276d7..e230e930f1ec 100644 --- a/resources/lang/es_ES/texts.php +++ b/resources/lang/es_ES/texts.php @@ -705,6 +705,25 @@ return array( 'login' => 'Login', 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + ); \ No newline at end of file diff --git a/resources/lang/fr/texts.php b/resources/lang/fr/texts.php index 010c5557709c..e16f140f5eb3 100644 --- a/resources/lang/fr/texts.php +++ b/resources/lang/fr/texts.php @@ -697,6 +697,25 @@ return array( 'login' => 'Connexion', 'or' => 'ou', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + ); diff --git a/resources/lang/fr_CA/texts.php b/resources/lang/fr_CA/texts.php index 3e808ab19e7f..724cf87a4321 100644 --- a/resources/lang/fr_CA/texts.php +++ b/resources/lang/fr_CA/texts.php @@ -698,5 +698,24 @@ return array( 'login' => 'Login', 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + ); diff --git a/resources/lang/it/texts.php b/resources/lang/it/texts.php index 8b5a3e5047de..4892d732c3f4 100644 --- a/resources/lang/it/texts.php +++ b/resources/lang/it/texts.php @@ -700,6 +700,25 @@ return array( 'login' => 'Login', 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + ); diff --git a/resources/lang/lt/texts.php b/resources/lang/lt/texts.php index 66f56de588f8..eaf8bbb9e5d7 100644 --- a/resources/lang/lt/texts.php +++ b/resources/lang/lt/texts.php @@ -707,6 +707,25 @@ return array( 'login' => 'Login', 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + ); diff --git a/resources/lang/nb_NO/texts.php b/resources/lang/nb_NO/texts.php index a5d9d569da21..1e2a21df14e2 100644 --- a/resources/lang/nb_NO/texts.php +++ b/resources/lang/nb_NO/texts.php @@ -705,6 +705,25 @@ return array( 'login' => 'Login', 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + ); \ No newline at end of file diff --git a/resources/lang/nl/texts.php b/resources/lang/nl/texts.php index 6dad49264bfe..f4e64362a2aa 100644 --- a/resources/lang/nl/texts.php +++ b/resources/lang/nl/texts.php @@ -700,6 +700,25 @@ return array( 'login' => 'Login', 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + ); diff --git a/resources/lang/pt_BR/texts.php b/resources/lang/pt_BR/texts.php index be2080b68341..bfa9c57fa910 100644 --- a/resources/lang/pt_BR/texts.php +++ b/resources/lang/pt_BR/texts.php @@ -700,5 +700,24 @@ return array( 'login' => 'Login', 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + ); diff --git a/resources/lang/sv/texts.php b/resources/lang/sv/texts.php index 86a9de4b8471..e26f1ad515dc 100644 --- a/resources/lang/sv/texts.php +++ b/resources/lang/sv/texts.php @@ -703,6 +703,25 @@ return array( 'login' => 'Login', 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'created_by_recurring' => 'Created by recurring invoice :invoice', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'old_browser' => 'Please use a newer browser', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'Times', + 'set_now' => 'Set now', + 'dark_mode' => 'Dark Mode', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + ); diff --git a/resources/views/accounts/account_gateway.blade.php b/resources/views/accounts/account_gateway.blade.php index 13d6cd337e27..2aa79c2dbb68 100644 --- a/resources/views/accounts/account_gateway.blade.php +++ b/resources/views/accounts/account_gateway.blade.php @@ -14,9 +14,12 @@
@if ($accountGateway) - {!! Former::populateField('payment_type_id', $paymentTypeId) !!} {!! Former::populateField('gateway_id', $accountGateway->gateway_id) !!} - {!! Former::populateField('recommendedGateway_id', $accountGateway->gateway_id) !!} + {!! Former::populateField('payment_type_id', $paymentTypeId) !!} + {!! Former::populateField('recommendedGateway_id', $accountGateway->gateway_id) !!} + {!! Former::populateField('show_address', intval($accountGateway->show_address)) !!} + {!! Former::populateField('update_address', intval($accountGateway->update_address)) !!} + @if ($config) @foreach ($accountGateway->fields as $field => $junk) @if (in_array($field, $hiddenFields)) @@ -28,6 +31,8 @@ @endif @else {!! Former::populateField('gateway_id', GATEWAY_STRIPE) !!} + {!! Former::populateField('show_address', 1) !!} + {!! Former::populateField('update_address', 1) !!} @endif {!! Former::select('payment_type_id') @@ -77,6 +82,15 @@ @endforeach + {!! Former::checkbox('show_address') + ->label(trans('texts.billing_address')) + ->text(trans('texts.show_address_help')) + ->addGroupClass('gateway-option') !!} + {!! Former::checkbox('update_address') + ->label(' ') + ->text(trans('texts.update_address_help')) + ->addGroupClass('gateway-option') !!} + {!! Former::checkboxes('creditCardTypes[]') ->label('Accepted Credit Cards') ->checkboxes($creditCardTypes) @@ -131,11 +145,25 @@ } } + function enableUpdateAddress(event) { + var disabled = !$('#show_address').is(':checked'); + $('#update_address').prop('disabled', disabled); + $('label[for=update_address]').css('color', disabled ? '#888' : '#000'); + if (disabled) { + $('#update_address').prop('checked', false); + } else if (event) { + $('#update_address').prop('checked', true); + } + } + $(function() { setPaymentType(); @if ($accountGateway) $('.payment-type-option').hide(); @endif + + $('#show_address').change(enableUpdateAddress); + enableUpdateAddress(); }) diff --git a/resources/views/accounts/details.blade.php b/resources/views/accounts/details.blade.php index feef5eceb57e..90475d384b75 100644 --- a/resources/views/accounts/details.blade.php +++ b/resources/views/accounts/details.blade.php @@ -22,6 +22,9 @@ {{ Former::populateField('last_name', $account->users()->first()->last_name) }} {{ Former::populateField('email', $account->users()->first()->email) }} {{ Former::populateField('phone', $account->users()->first()->phone) }} + @if (Utils::isNinja()) + {{ Former::populateField('dark_mode', intval($account->users()->first()->dark_mode)) }} + @endif @endif
@@ -88,6 +91,10 @@ {!! Former::text('last_name') !!} {!! Former::text('email') !!} {!! Former::text('phone') !!} + @if (Utils::isNinja()) + {!! Former::checkbox('dark_mode')->text(trans('texts.dark_mode_help')) !!} + @endif + @if (Auth::user()->confirmed) {!! Former::actions( Button::primary(trans('texts.change_password'))->small()->withAttributes(['onclick'=>'showChangePassword()'])) !!} @elseif (Auth::user()->registered) diff --git a/resources/views/accounts/invoice_settings.blade.php b/resources/views/accounts/invoice_settings.blade.php index b46bdfd17b0c..b3b03011578c 100644 --- a/resources/views/accounts/invoice_settings.blade.php +++ b/resources/views/accounts/invoice_settings.blade.php @@ -23,8 +23,7 @@ {{ Former::populateField('custom_invoice_taxes2', intval($account->custom_invoice_taxes2)) }} {{ Former::populateField('share_counter', intval($account->share_counter)) }} {{ Former::populateField('pdf_email_attachment', intval($account->pdf_email_attachment)) }} - {{ Former::populateField('utf8_invoices', intval($account->utf8_invoices)) }} - {{ Former::populateField('auto_wrap', intval($account->auto_wrap)) }} + {{ Former::populateField('utf8_invoices', intval($account->utf8_invoices)) }}
@@ -99,9 +98,6 @@
{!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!} {!! Former::checkbox('utf8_invoices')->text(trans('texts.enable')) !!} -
- {!! Former::checkbox('auto_wrap')->text(trans('texts.enable')) !!} -
diff --git a/resources/views/header.blade.php b/resources/views/header.blade.php index f4fc371e66ce..857cbb6b0664 100644 --- a/resources/views/header.blade.php +++ b/resources/views/header.blade.php @@ -25,6 +25,23 @@ } } + @if (Auth::check() && Auth::user()->dark_mode) + body { + background: #000 !important; + color: white !important; + } + + .panel-body { + background: #272822 !important; + /*background: #e6e6e6 !important;*/ + } + + .panel-default { + border-color: #444; + } + @endif + + @include('script') @@ -309,6 +326,15 @@ showSignUp(); @endif + $('ul.navbar-settings, ul.navbar-history').hover(function () { + //$('.user-accounts').find('li').hide(); + //$('.user-accounts').css({display: 'none'}); + //console.log($('.user-accounts').dropdown('')) + if ($('.user-accounts').css('display') == 'block') { + $('.user-accounts').dropdown('toggle'); + } + }); + @yield('onReady') }); @@ -409,7 +435,7 @@
-
-

 

- {!! Former::actions( - Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->onchange('onSelectChange()')->raw(), - Button::success(trans('texts.save'))->withAttributes(['onclick' => 'submitForm()'])->large()->appendIcon(Icon::create('floppy-disk')) - ) !!} +
+ {!! Former::select('invoice_design_id')->style('display:inline;width:120px')->fromQuery($invoiceDesigns, 'name', 'id')->onchange('onSelectChange()')->raw() !!} +
+ {!! Button::normal(trans('texts.documentation'))->asLinkTo(PDFMAKE_DOCS)->withAttributes(['target' => '_blank'])->appendIcon(Icon::create('info-sign')) !!} + {!! Button::normal(trans('texts.cancel'))->asLinkTo(URL::to('/company/advanced_settings/invoice_design'))->appendIcon(Icon::create('remove-circle')) !!} + {!! Button::success(trans('texts.save'))->withAttributes(['onclick' => 'submitForm()'])->appendIcon(Icon::create('floppy-disk')) !!} +
+
@if (!Auth::user()->isPro()) + +@stop \ No newline at end of file From 5249e24fa26c9f1c1ca583dd6c2a1f167b52bbc6 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 3 Aug 2015 10:26:31 +0300 Subject: [PATCH 24/62] Git push --- app/Http/routes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/routes.php b/app/Http/routes.php index e016e3841c09..dc58f5d14eb1 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -2,6 +2,7 @@ //dd("We're currently undergoing a brief maintenance, we'll be right back."); + /* |-------------------------------------------------------------------------- | Application Routes From 29e90cae0b29616663cbd9d5492759cd5b4c3d83 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 3 Aug 2015 11:02:35 +0300 Subject: [PATCH 25/62] Minor fix --- app/Http/Middleware/StartupCheck.php | 2 +- app/Http/routes.php | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/Http/Middleware/StartupCheck.php b/app/Http/Middleware/StartupCheck.php index a6c2741977c8..a0b7fd2b1d09 100644 --- a/app/Http/Middleware/StartupCheck.php +++ b/app/Http/Middleware/StartupCheck.php @@ -158,7 +158,7 @@ class StartupCheck } } - if (preg_match('/(?i)msie [2-8]/', $_SERVER['HTTP_USER_AGENT'])) { + if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/(?i)msie [2-8]/', $_SERVER['HTTP_USER_AGENT'])) { Session::flash('error', trans('texts.old_browser')); } diff --git a/app/Http/routes.php b/app/Http/routes.php index dc58f5d14eb1..a9c322632db0 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -1,8 +1,5 @@ Date: Mon, 3 Aug 2015 11:52:47 +0300 Subject: [PATCH 26/62] Disabled datepicker localization --- app/Http/Controllers/PaymentController.php | 1 + resources/views/master.blade.php | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 6632ce843b3b..712f19dce6c5 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -389,6 +389,7 @@ class PaymentController extends BaseController 'currencyId' => 1, 'paymentTitle' => $affiliate->payment_title, 'paymentSubtitle' => $affiliate->payment_subtitle, + 'showAddress' => true, ]; return View::make('payments.payment', $data); diff --git a/resources/views/master.blade.php b/resources/views/master.blade.php index 59a486405da7..b9dec31654c2 100644 --- a/resources/views/master.blade.php +++ b/resources/views/master.blade.php @@ -53,10 +53,13 @@ 'sSearch': '' } } ); - + + /* $.extend( true, $.fn.datepicker.defaults, { language:'{{App::getLocale()}}' }); + */ + From 4b7d14363038ea62b7144b24909753e38b570515 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 3 Aug 2015 22:08:07 +0300 Subject: [PATCH 27/62] Minor fixes --- app/Http/Controllers/Auth/AuthController.php | 2 +- app/Http/Controllers/UserController.php | 6 +----- app/Libraries/Utils.php | 6 ++++++ app/Models/Account.php | 2 +- app/Ninja/Repositories/AccountRepository.php | 6 +++++- public/js/built.js | 2 +- public/js/script.js | 2 +- resources/views/accounts/details.blade.php | 2 +- resources/views/header.blade.php | 6 +++--- resources/views/invoices/edit.blade.php | 2 +- resources/views/user_account.blade.php | 2 +- 11 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index e17135fe7146..a5897ac6879d 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -62,7 +62,7 @@ class AuthController extends Controller { $userId = Auth::check() ? Auth::user()->id : null; $user = User::where('email', '=', $request->input('email'))->first(); - if ($user->failed_logins >= 3) { + if ($user && $user->failed_logins >= 3) { Session::flash('error', 'These credentials do not match our records.'); return redirect()->to('login'); } diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 50b66b69f720..85b4cede7d9a 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -382,10 +382,6 @@ class UserController extends BaseController public function manageCompanies() { - $data = [ - - ]; - - return View::make('users.account_management', $data); + return View::make('users.account_management'); } } diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index b38911371476..1efcf4b229a8 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -3,6 +3,7 @@ use Auth; use Cache; use DB; +use App; use Schema; use Session; use Request; @@ -69,6 +70,11 @@ class Utils return Auth::check() && Auth::user()->isPro(); } + public static function isEnglish() + { + return App::getLocale() == 'en'; + } + public static function getUserType() { if (Utils::isNinja()) { diff --git a/app/Models/Account.php b/app/Models/Account.php index be307f6ca775..e8cde5293c09 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -147,7 +147,7 @@ class Account extends Eloquent public function getLogoPath() { $fileName = 'logo/' . $this->account_key; - return file_exists($fileName.'.png') ? $fileName.'.png' : $fileName.'.jpg'; + return file_exists($fileName.'.png') && $this->utf8_invoices ? $fileName.'.png' : $fileName.'.jpg'; } public function getLogoWidth() diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index 98ef973f674f..222463c4b532 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -6,7 +6,7 @@ use Session; use Utils; use DB; use stdClass; - +use Schema; use App\Models\AccountGateway; use App\Models\Invitation; use App\Models\Invoice; @@ -250,6 +250,10 @@ class AccountRepository public function findUserAccounts($userId1, $userId2 = false) { + if (!Schema::hasTable('user_accounts')) { + return false; + } + $query = UserAccount::where('user_id1', '=', $userId1) ->orWhere('user_id2', '=', $userId1) ->orWhere('user_id3', '=', $userId1) diff --git a/public/js/built.js b/public/js/built.js index 0a22d2a5a35e..a47d137523b7 100644 --- a/public/js/built.js +++ b/public/js/built.js @@ -29884,7 +29884,7 @@ function generatePDF(invoice, javascript, force, cb) { if (!invoice || !javascript) { return; } - console.log('== generatePDF - force: %s', force); + //console.log('== generatePDF - force: %s', force); if (force || !invoiceOld) { refreshTimer = null; } else { diff --git a/public/js/script.js b/public/js/script.js index dded462d101a..eabb1be9f671 100644 --- a/public/js/script.js +++ b/public/js/script.js @@ -13,7 +13,7 @@ function generatePDF(invoice, javascript, force, cb) { if (!invoice || !javascript) { return; } - console.log('== generatePDF - force: %s', force); + //console.log('== generatePDF - force: %s', force); if (force || !invoiceOld) { refreshTimer = null; } else { diff --git a/resources/views/accounts/details.blade.php b/resources/views/accounts/details.blade.php index 89a2a179f94b..b433f55b7353 100644 --- a/resources/views/accounts/details.blade.php +++ b/resources/views/accounts/details.blade.php @@ -51,7 +51,7 @@ @if (file_exists($account->getLogoPath()))
- {!! HTML::image($account->getLogoPath().'?no_cache='.time(), "Logo") !!}   + {!! HTML::image($account->getLogoPath().'?no_cache='.time(), 'Logo', ['width' => 200]) !!}   {{ trans('texts.remove_logo') }}

@endif diff --git a/resources/views/header.blade.php b/resources/views/header.blade.php index e42e71f27574..9abb87690796 100644 --- a/resources/views/header.blade.php +++ b/resources/views/header.blade.php @@ -253,12 +253,12 @@ }, 2000); $('#search').blur(function(){ - $('#search').css('width', '150px'); + $('#search').css('width', '{{ Utils::isEnglish() ? 150 : 100 }}px'); $('ul.navbar-right').show(); }); $('#search').focus(function(){ - $('#search').css('width', '256px'); + $('#search').css('width', '{{ Utils::isEnglish() ? 256 : 206 }}px'); $('ul.navbar-right').hide(); if (!window.hasOwnProperty('searchData')) { $.get('{{ URL::route('getSearchData') }}', function(data) { @@ -461,7 +461,7 @@ diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index 317b72965392..db574f26b340 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -94,7 +94,7 @@ {!! Former::text('end_date')->data_bind("datePicker: end_date, valueUpdate: 'afterkeydown'") ->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->append('') !!} - @if ($invoice && $invoice->recurring_invoice_id) + @if ($invoice && $invoice->recurring_invoice)
{!! trans('texts.created_by_recurring', ['invoice' => link_to('/invoices/'.$invoice->recurring_invoice->public_id, $invoice->recurring_invoice->invoice_number)]) !!}
diff --git a/resources/views/user_account.blade.php b/resources/views/user_account.blade.php index 7364f33f6afc..9384f621b498 100644 --- a/resources/views/user_account.blade.php +++ b/resources/views/user_account.blade.php @@ -1,5 +1,5 @@
  • - @if (isset($user_id)) + @if (isset($user_id) && $user_id != Auth::user()->id) @else From 8f80ccf4d792db071f76274ce306f3d401ae5533 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 4 Aug 2015 14:38:48 +0300 Subject: [PATCH 28/62] Minor fixes --- app/Http/Controllers/AppController.php | 2 +- app/Ninja/Repositories/AccountRepository.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/AppController.php b/app/Http/Controllers/AppController.php index b135c045053e..bc6c699e728b 100644 --- a/app/Http/Controllers/AppController.php +++ b/app/Http/Controllers/AppController.php @@ -176,7 +176,7 @@ class AppController extends BaseController public function update() { - if (!Utils::isNinja() && Auth::check()) { + if (!Utils::isNinja()) { try { Artisan::call('migrate', array('--force' => true)); Artisan::call('db:seed', array('--force' => true, '--class' => 'PaymentLibrariesSeeder')); diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index 222463c4b532..5aaa6e8d4d90 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -316,6 +316,9 @@ class AccountRepository } public function syncUserAccounts($users, $proPlanPaid = false) { + if (!$users) { + return; + } if (!$proPlanPaid) { foreach ($users as $user) { From 2cfcdd1e777ebb8472759f5ef10b8a512e2e5653 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 4 Aug 2015 17:33:30 +0300 Subject: [PATCH 29/62] Minor improvements --- app/Http/Controllers/HomeController.php | 4 ++-- resources/lang/da/texts.php | 2 +- resources/lang/de/texts.php | 2 +- resources/lang/en/texts.php | 3 ++- resources/lang/es/texts.php | 2 +- resources/lang/es_ES/texts.php | 2 +- resources/lang/fr/texts.php | 2 +- resources/lang/fr_CA/texts.php | 2 +- resources/lang/it/texts.php | 2 +- resources/lang/lt/texts.php | 2 +- resources/lang/nb_NO/texts.php | 2 +- resources/lang/nl/texts.php | 2 +- resources/lang/pt_BR/texts.php | 2 +- resources/lang/sv/texts.php | 2 +- resources/views/dashboard.blade.php | 6 +++--- resources/views/list.blade.php | 7 +++++-- 16 files changed, 24 insertions(+), 20 deletions(-) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 2645b0cbd98a..0f8226f077b1 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -72,9 +72,9 @@ class HomeController extends BaseController $user->news_feed_id = $newsFeedId; $user->save(); } - - Session::forget('news_feed_message'); } + + Session::forget('news_feed_message'); return 'success'; } diff --git a/resources/lang/da/texts.php b/resources/lang/da/texts.php index 580e366e7298..ced550498886 100644 --- a/resources/lang/da/texts.php +++ b/resources/lang/da/texts.php @@ -743,7 +743,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', diff --git a/resources/lang/de/texts.php b/resources/lang/de/texts.php index 7519119b40d6..9cd8866c9276 100644 --- a/resources/lang/de/texts.php +++ b/resources/lang/de/texts.php @@ -733,7 +733,7 @@ return array( 'invoice_no' => 'Rechnung Nr.', 'recent_payments' => 'Kürzliche Zahlungen', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', ); \ No newline at end of file diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index b9807c4c93b4..23b684fb2577 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -120,7 +120,7 @@ return array( 'active_clients' => 'active clients', 'invoices_past_due' => 'Invoices Past Due', 'upcoming_invoices' => 'Upcoming Invoices', - 'average_invoice' => 'Average invoice', + 'average_invoice' => 'Average Invoice', // list pages 'archive' => 'Archive', @@ -741,6 +741,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', + 'total_revenue' => 'Total Revenue', ); diff --git a/resources/lang/es/texts.php b/resources/lang/es/texts.php index 92ca314723b6..cf290277618c 100644 --- a/resources/lang/es/texts.php +++ b/resources/lang/es/texts.php @@ -713,7 +713,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', ); \ No newline at end of file diff --git a/resources/lang/es_ES/texts.php b/resources/lang/es_ES/texts.php index 97b333fbb2c2..8d6fd83a7f9a 100644 --- a/resources/lang/es_ES/texts.php +++ b/resources/lang/es_ES/texts.php @@ -742,7 +742,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', diff --git a/resources/lang/fr/texts.php b/resources/lang/fr/texts.php index 6fd60e17c4ba..6a87a6e78896 100644 --- a/resources/lang/fr/texts.php +++ b/resources/lang/fr/texts.php @@ -734,7 +734,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', diff --git a/resources/lang/fr_CA/texts.php b/resources/lang/fr_CA/texts.php index 0c0bf2e3a4a2..05d56c57821c 100644 --- a/resources/lang/fr_CA/texts.php +++ b/resources/lang/fr_CA/texts.php @@ -735,7 +735,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', ); diff --git a/resources/lang/it/texts.php b/resources/lang/it/texts.php index 9672b9ea533d..061bd060be8e 100644 --- a/resources/lang/it/texts.php +++ b/resources/lang/it/texts.php @@ -737,7 +737,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', diff --git a/resources/lang/lt/texts.php b/resources/lang/lt/texts.php index 15ca60fcba36..ba4f8715e5a1 100644 --- a/resources/lang/lt/texts.php +++ b/resources/lang/lt/texts.php @@ -744,7 +744,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', diff --git a/resources/lang/nb_NO/texts.php b/resources/lang/nb_NO/texts.php index c014c917637c..eeb80190ad5a 100644 --- a/resources/lang/nb_NO/texts.php +++ b/resources/lang/nb_NO/texts.php @@ -742,7 +742,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', diff --git a/resources/lang/nl/texts.php b/resources/lang/nl/texts.php index f82175f36ad9..aa8458c590ee 100644 --- a/resources/lang/nl/texts.php +++ b/resources/lang/nl/texts.php @@ -737,7 +737,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', diff --git a/resources/lang/pt_BR/texts.php b/resources/lang/pt_BR/texts.php index 875691412797..8bb24e732859 100644 --- a/resources/lang/pt_BR/texts.php +++ b/resources/lang/pt_BR/texts.php @@ -737,7 +737,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', ); diff --git a/resources/lang/sv/texts.php b/resources/lang/sv/texts.php index 13e443f81bdc..09aaa7998718 100644 --- a/resources/lang/sv/texts.php +++ b/resources/lang/sv/texts.php @@ -740,7 +740,7 @@ return array( 'recent_payments' => 'Recent Payments', 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', - + 'total_revenue' => 'Total Revenue', diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 864e7aeb1f89..f4e52f16e67f 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -7,6 +7,9 @@
    +
    + {{ trans('texts.total_revenue') }} +
    @if (count($paidToDate)) @foreach ($paidToDate as $item) @@ -16,9 +19,6 @@ {{ Utils::formatMoney(0) }} @endif
    -
    - {{ trans('texts.in_total_revenue') }} -
    diff --git a/resources/views/list.blade.php b/resources/views/list.blade.php index cc9d3e35ce99..915c176f8c06 100644 --- a/resources/views/list.blade.php +++ b/resources/views/list.blade.php @@ -24,13 +24,16 @@
    - + @if (Auth::user()->isPro() && $entityType == ENTITY_INVOICE) {!! Button::normal(trans('texts.quotes'))->asLinkTo(URL::to('/quotes'))->appendIcon(Icon::create('list')) !!} @elseif ($entityType == ENTITY_CLIENT) {!! Button::normal(trans('texts.credits'))->asLinkTo(URL::to('/credits'))->appendIcon(Icon::create('list')) !!} @endif - {!! Button::primary(trans("texts.new_$entityType"))->asLinkTo(URL::to("/{$entityType}s/create"))->appendIcon(Icon::create('plus-sign')) !!} + + @if ($entityType != ENTITY_TASK || Auth::user()->account->timezone_id) + {!! Button::primary(trans("texts.new_$entityType"))->asLinkTo(URL::to("/{$entityType}s/create"))->appendIcon(Icon::create('plus-sign')) !!} + @endif
    @if (isset($secEntityType)) From 07b3fdb30cee2fde4be6c7986db0cedd9d0b85aa Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Fri, 7 Aug 2015 09:14:29 +0300 Subject: [PATCH 30/62] Minor improvements --- app/Http/routes.php | 3 +- app/Libraries/Utils.php | 9 +++--- app/Models/Account.php | 1 + app/Ninja/Repositories/AccountRepository.php | 2 +- public/js/built.js | 9 +++--- public/js/pdf.pdfmake.js | 6 ++-- public/js/script.js | 3 -- resources/lang/en/texts.php | 2 ++ resources/views/header.blade.php | 11 ++++--- resources/views/user_account.blade.php | 4 +-- .../views/users/account_management.blade.php | 29 ++++++++++++++++--- 11 files changed, 51 insertions(+), 28 deletions(-) diff --git a/app/Http/routes.php b/app/Http/routes.php index a9c322632db0..706ddbd3bb9e 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -1,5 +1,6 @@ id === 1) { Auth::loginUsingId(1); } -*/ +*/ \ No newline at end of file diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 1efcf4b229a8..1263b4ed6763 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -341,10 +341,11 @@ class Utils return; } - $timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE); + //$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE); $format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT); - $dateTime = DateTime::createFromFormat($format, $date, new DateTimeZone($timezone)); + //$dateTime = DateTime::createFromFormat($format, $date, new DateTimeZone($timezone)); + $dateTime = DateTime::createFromFormat($format, $date); return $formatResult ? $dateTime->format('Y-m-d') : $dateTime; } @@ -355,11 +356,11 @@ class Utils return ''; } - $timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE); + //$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE); $format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT); $dateTime = DateTime::createFromFormat('Y-m-d', $date); - $dateTime->setTimeZone(new DateTimeZone($timezone)); + //$dateTime->setTimeZone(new DateTimeZone($timezone)); return $formatResult ? $dateTime->format($format) : $dateTime; } diff --git a/app/Models/Account.php b/app/Models/Account.php index e8cde5293c09..ee88e2fd1fcb 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -147,6 +147,7 @@ class Account extends Eloquent public function getLogoPath() { $fileName = 'logo/' . $this->account_key; + return file_exists($fileName.'.png') && $this->utf8_invoices ? $fileName.'.png' : $fileName.'.jpg'; } diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index 5aaa6e8d4d90..d33c08f7abdf 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -298,7 +298,7 @@ class AccountRepository $item->account_id = $user->account->id; $item->account_name = $user->account->getDisplayName(); $item->pro_plan_paid = $user->account->pro_plan_paid; - $item->account_key = file_exists($user->account->getLogoPath()) ? $user->account->account_key : null; + $item->logo_path = file_exists($user->account->getLogoPath()) ? $user->account->getLogoPath() : null; $data[] = $item; } diff --git a/public/js/built.js b/public/js/built.js index a47d137523b7..08fa79b13021 100644 --- a/public/js/built.js +++ b/public/js/built.js @@ -30748,9 +30748,6 @@ function displayGrid(doc, invoice, data, x, y, layout, options) { key = invoice.account[key]; } else if (key === 'tax' && invoice.tax_name) { key = invoice.tax_name + ' ' + (invoice.tax_rate*1).toString() + '%'; - if (invoice.tax_name.toLowerCase().indexOf(invoiceLabels['tax'].toLowerCase()) == -1) { - key = invoiceLabels['tax'] + ': ' + key; - } } else if (key === 'discount' && NINJA.parseFloat(invoice.discount) && !parseInt(invoice.is_amount_discount)) { key = invoiceLabels[key] + ' ' + parseFloat(invoice.discount) + '%'; } else { @@ -31619,7 +31616,7 @@ NINJA.decodeJavascript = function(invoice, javascript) } // search/replace values - var regExp = new RegExp('"\\$\\\w*?Value"', 'g'); + var regExp = new RegExp('"\\$[\\\w\\\.]*?Value"', 'g'); var matches = javascript.match(regExp); if (matches) { @@ -31628,6 +31625,7 @@ NINJA.decodeJavascript = function(invoice, javascript) field = match.substring(2, match.indexOf('Value')); field = toSnakeCase(field); var value = getDescendantProp(invoice, field) || ' '; + if (field.toLowerCase().indexOf('date') >= 0 && value != ' ') { value = moment(value, 'YYYY-MM-DD').format('MMM D YYYY'); } @@ -31760,7 +31758,8 @@ NINJA.subtotals = function(invoice, removeBalance) } if (invoice.tax && invoice.tax.name || invoice.tax_name) { - data.push([{text: invoiceLabels.tax}, {text: formatMoney(invoice.tax_amount, invoice.client.currency_id)}]); + var taxStr = invoice.tax_name + ' ' + (invoice.tax_rate*1).toString() + '%'; + data.push([{text: taxStr}, {text: formatMoney(invoice.tax_amount, invoice.client.currency_id)}]); } if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') { diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index 168a8733567b..8ec21c1e5b08 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -142,7 +142,7 @@ NINJA.decodeJavascript = function(invoice, javascript) } // search/replace values - var regExp = new RegExp('"\\$\\\w*?Value"', 'g'); + var regExp = new RegExp('"\\$[\\\w\\\.]*?Value"', 'g'); var matches = javascript.match(regExp); if (matches) { @@ -151,6 +151,7 @@ NINJA.decodeJavascript = function(invoice, javascript) field = match.substring(2, match.indexOf('Value')); field = toSnakeCase(field); var value = getDescendantProp(invoice, field) || ' '; + if (field.toLowerCase().indexOf('date') >= 0 && value != ' ') { value = moment(value, 'YYYY-MM-DD').format('MMM D YYYY'); } @@ -283,7 +284,8 @@ NINJA.subtotals = function(invoice, removeBalance) } if (invoice.tax && invoice.tax.name || invoice.tax_name) { - data.push([{text: invoiceLabels.tax}, {text: formatMoney(invoice.tax_amount, invoice.client.currency_id)}]); + var taxStr = invoice.tax_name + ' ' + (invoice.tax_rate*1).toString() + '%'; + data.push([{text: taxStr}, {text: formatMoney(invoice.tax_amount, invoice.client.currency_id)}]); } if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') { diff --git a/public/js/script.js b/public/js/script.js index eabb1be9f671..221b8d0a0d38 100644 --- a/public/js/script.js +++ b/public/js/script.js @@ -877,9 +877,6 @@ function displayGrid(doc, invoice, data, x, y, layout, options) { key = invoice.account[key]; } else if (key === 'tax' && invoice.tax_name) { key = invoice.tax_name + ' ' + (invoice.tax_rate*1).toString() + '%'; - if (invoice.tax_name.toLowerCase().indexOf(invoiceLabels['tax'].toLowerCase()) == -1) { - key = invoiceLabels['tax'] + ': ' + key; - } } else if (key === 'discount' && NINJA.parseFloat(invoice.discount) && !parseInt(invoice.is_amount_discount)) { key = invoiceLabels[key] + ' ' + parseFloat(invoice.discount) + '%'; } else { diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 23b684fb2577..1a159cb2c5bb 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -742,6 +742,8 @@ return array( 'outstanding' => 'Outstanding', 'manage_companies' => 'Manage Companies', 'total_revenue' => 'Total Revenue', + + 'current_user' => 'Current User', ); diff --git a/resources/views/header.blade.php b/resources/views/header.blade.php index 9abb87690796..6925992c315f 100644 --- a/resources/views/header.blade.php +++ b/resources/views/header.blade.php @@ -385,7 +385,7 @@ 'user_id' => $item->user_id, 'account_name' => $item->account_name, 'user_name' => $item->user_name, - 'account_key' => $item->account_key, + 'logo_path' => isset($item->logo_path) ? $item->logo_path : "", 'selected' => true, ]) @endif @@ -397,7 +397,7 @@ 'user_id' => $item->user_id, 'account_name' => $item->account_name, 'user_name' => $item->user_name, - 'account_key' => $item->account_key, + 'logo_path' => isset($item->logo_path) ? $item->logo_path : "", 'selected' => false, ]) @endif @@ -406,16 +406,15 @@ @include('user_account', [ 'account_name' => Auth::user()->account->name ?: trans('texts.untitled'), 'user_name' => Auth::user()->getDisplayName(), - 'account_key' => Auth::user()->account->account_key, + 'logo_path' => Auth::user()->account->getLogoPath(), 'selected' => true, ]) @endif
  • - @if (!session(SESSION_USER_ACCOUNTS) || count(session(SESSION_USER_ACCOUNTS)) < 5) -
  • {!! link_to('/login?new_company=true', trans('texts.add_company')) !!}
  • - @endif @if (count(session(SESSION_USER_ACCOUNTS)) > 1)
  • {!! link_to('/manage_companies', trans('texts.manage_companies')) !!}
  • + @elseif (!session(SESSION_USER_ACCOUNTS) || count(session(SESSION_USER_ACCOUNTS)) < 5) +
  • {!! link_to('/login?new_company=true', trans('texts.add_company')) !!}
  • @endif
  • {!! link_to('#', trans('texts.logout'), array('onclick'=>'logout()')) !!}
  • diff --git a/resources/views/user_account.blade.php b/resources/views/user_account.blade.php index 9384f621b498..4848a190e5c9 100644 --- a/resources/views/user_account.blade.php +++ b/resources/views/user_account.blade.php @@ -5,9 +5,9 @@
    @endif - @if (file_exists('logo/'.$account_key.'.jpg')) + @if (file_exists($logo_path))
    - +
    @else
     
    diff --git a/resources/views/users/account_management.blade.php b/resources/views/users/account_management.blade.php index 75fa8a500ea7..3839a0bb4377 100644 --- a/resources/views/users/account_management.blade.php +++ b/resources/views/users/account_management.blade.php @@ -2,18 +2,39 @@ @section('content') + +
    + {!! Button::success(trans('texts.add_company'))->asLinkTo('/login?new_company=true') !!} +
    +

     

    +
    +
    +
    +
    +
    - @foreach (Session::get(SESSION_USER_ACCOUNTS) as $account) + @foreach (Session::get(SESSION_USER_ACCOUNTS) as $account) - - - + + + @endforeach
    {{ $account->account_name }}{{ $account->user_name }}{!! Button::primary(trans('texts.unlink'))->small()->withAttributes(['onclick'=>"return showUnlink({$account->id}, {$account->user_id})"]) !!} + @if (isset($account->logo_path)) + {!! HTML::image($account->logo_path.'?no_cache='.time(), 'Logo', ['width' => 100]) !!} + @endif + +

    {{ $account->account_name }}
    + {{ $account->user_name }} + @if ($account->user_id == Auth::user()->id) + | {{ trans('texts.current_user')}} + @endif +

    +
    {!! Button::primary(trans('texts.unlink'))->withAttributes(['onclick'=>"return showUnlink({$account->id}, {$account->user_id})"]) !!}
    From 78bf49cd19fd8f35635ca4266328371eecc88657 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 10 Aug 2015 18:48:41 +0300 Subject: [PATCH 31/62] Recurring invoices no longer prevent invoice numbers from being sequential --- .../Commands/SendRecurringInvoices.php | 73 ++--------------- app/Http/Controllers/InvoiceController.php | 24 +++++- app/Http/Controllers/QuoteController.php | 3 +- app/Http/routes.php | 3 +- app/Models/Client.php | 4 - app/Models/EntityModel.php | 7 +- app/Models/Invoice.php | 11 ++- app/Ninja/Repositories/InvoiceRepository.php | 75 ++++++++++++++++- app/Providers/AppServiceProvider.php | 7 +- ..._122647_add_partial_amount_to_invoices.php | 2 +- public/js/built.js | 4 +- public/js/pdf.pdfmake.js | 4 +- resources/lang/da/texts.php | 7 +- resources/lang/de/texts.php | 7 +- resources/lang/en/texts.php | 7 +- resources/lang/es/texts.php | 7 +- resources/lang/es_ES/texts.php | 7 +- resources/lang/fr/texts.php | 7 +- resources/lang/fr_CA/texts.php | 7 +- resources/lang/it/texts.php | 7 +- resources/lang/lt/texts.php | 7 +- resources/lang/nb_NO/texts.php | 7 +- resources/lang/nl/texts.php | 7 +- resources/lang/pt_BR/texts.php | 7 +- resources/lang/sv/texts.php | 7 +- resources/views/invoices/edit.blade.php | 81 ++++++++++--------- storage/templates/bold.js | 4 +- storage/templates/clean.js | 13 +-- storage/templates/modern.js | 2 +- storage/templates/plain.js | 2 +- 30 files changed, 261 insertions(+), 149 deletions(-) diff --git a/app/Console/Commands/SendRecurringInvoices.php b/app/Console/Commands/SendRecurringInvoices.php index 8f65214f7fc1..8f31473c9eca 100644 --- a/app/Console/Commands/SendRecurringInvoices.php +++ b/app/Console/Commands/SendRecurringInvoices.php @@ -7,6 +7,7 @@ use Illuminate\Console\Command; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; use App\Ninja\Mailers\ContactMailer as Mailer; +use App\Ninja\Repositories\InvoiceRepository; use App\Models\Invoice; use App\Models\InvoiceItem; use App\Models\Invitation; @@ -16,12 +17,14 @@ class SendRecurringInvoices extends Command protected $name = 'ninja:send-invoices'; protected $description = 'Send recurring invoices'; protected $mailer; + protected $invoiceRepo; - public function __construct(Mailer $mailer) + public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo) { parent::__construct(); $this->mailer = $mailer; + $this->invoiceRepo = $invoiceRepo; } public function fire() @@ -34,74 +37,10 @@ class SendRecurringInvoices extends Command $this->info(count($invoices).' recurring invoice(s) found'); foreach ($invoices as $recurInvoice) { - if ($recurInvoice->client->deleted_at) { - continue; - } - - if (!$recurInvoice->user->confirmed) { - continue; - } - - $this->info('Processing Invoice '.$recurInvoice->id.' - Should send '.($recurInvoice->shouldSendToday() ? 'YES' : 'NO')); - - if (!$recurInvoice->shouldSendToday()) { - continue; - } - - $invoice = Invoice::createNew($recurInvoice); - $invoice->client_id = $recurInvoice->client_id; - $invoice->recurring_invoice_id = $recurInvoice->id; - $invoice->invoice_number = $recurInvoice->account->getNextInvoiceNumber(false, 'R'); - $invoice->amount = $recurInvoice->amount; - $invoice->balance = $recurInvoice->amount; - $invoice->invoice_date = date_create()->format('Y-m-d'); - $invoice->discount = $recurInvoice->discount; - $invoice->po_number = $recurInvoice->po_number; - $invoice->public_notes = Utils::processVariables($recurInvoice->public_notes); - $invoice->terms = Utils::processVariables($recurInvoice->terms); - $invoice->invoice_footer = Utils::processVariables($recurInvoice->invoice_footer); - $invoice->tax_name = $recurInvoice->tax_name; - $invoice->tax_rate = $recurInvoice->tax_rate; - $invoice->invoice_design_id = $recurInvoice->invoice_design_id; - $invoice->custom_value1 = $recurInvoice->custom_value1; - $invoice->custom_value2 = $recurInvoice->custom_value2; - $invoice->custom_taxes1 = $recurInvoice->custom_taxes1; - $invoice->custom_taxes2 = $recurInvoice->custom_taxes2; - $invoice->is_amount_discount = $recurInvoice->is_amount_discount; - - if ($invoice->client->payment_terms != 0) { - $days = $invoice->client->payment_terms; - if ($days == -1) { - $days = 0; - } - $invoice->due_date = date_create()->modify($days.' day')->format('Y-m-d'); - } - - $invoice->save(); - - foreach ($recurInvoice->invoice_items as $recurItem) { - $item = InvoiceItem::createNew($recurItem); - $item->product_id = $recurItem->product_id; - $item->qty = $recurItem->qty; - $item->cost = $recurItem->cost; - $item->notes = Utils::processVariables($recurItem->notes); - $item->product_key = Utils::processVariables($recurItem->product_key); - $item->tax_name = $recurItem->tax_name; - $item->tax_rate = $recurItem->tax_rate; - $invoice->invoice_items()->save($item); - } - - foreach ($recurInvoice->invitations as $recurInvitation) { - $invitation = Invitation::createNew($recurInvitation); - $invitation->contact_id = $recurInvitation->contact_id; - $invitation->invitation_key = str_random(RANDOM_KEY_LENGTH); - $invoice->invitations()->save($invitation); - } + $this->info('Processing Invoice '.$recurInvoice->id.' - Should send '.($recurInvoice->shouldSendToday() ? 'YES' : 'NO')); + $this->invoiceRepo->createRecurringInvoice($recurInvoice); $this->mailer->sendInvoice($invoice); - - $recurInvoice->last_sent_date = Carbon::now()->toDateTimeString(); - $recurInvoice->save(); } $this->info('Done'); diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 248b1be6bef3..efed3effd150 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -280,7 +280,7 @@ class InvoiceController extends BaseController $method = 'POST'; $url = "{$entityType}s"; } else { - Utils::trackViewed($invoice->invoice_number.' - '.$invoice->client->getDisplayName(), $invoice->getEntityType()); + Utils::trackViewed($invoice->getDisplayName().' - '.$invoice->client->getDisplayName(), $invoice->getEntityType()); $method = 'PUT'; $url = "{$entityType}s/{$publicId}"; } @@ -335,6 +335,7 @@ class InvoiceController extends BaseController 'url' => $url, 'title' => trans("texts.edit_{$entityType}"), 'client' => $invoice->client, + 'isRecurring' => $invoice->is_recurring, 'actions' => $actions); $data = array_merge($data, self::getViewModel()); @@ -358,10 +359,10 @@ class InvoiceController extends BaseController return View::make('invoices.edit', $data); } - public function create($clientPublicId = 0) + public function create($clientPublicId = 0, $isRecurring = false) { $client = null; - $invoiceNumber = Auth::user()->account->getNextInvoiceNumber(); + $invoiceNumber = $isRecurring ? microtime(true) : Auth::user()->account->getNextInvoiceNumber(); if ($clientPublicId) { $client = Client::scope($clientPublicId)->firstOrFail(); @@ -375,12 +376,18 @@ class InvoiceController extends BaseController 'method' => 'POST', 'url' => 'invoices', 'title' => trans('texts.new_invoice'), + 'isRecurring' => $isRecurring, 'client' => $client); $data = array_merge($data, self::getViewModel()); return View::make('invoices.edit', $data); } + public function createRecurring($clientPublicId = 0) + { + return self::create($clientPublicId, true); + } + private static function getViewModel() { $recurringHelp = ''; @@ -510,7 +517,16 @@ class InvoiceController extends BaseController return $this->convertQuote($publicId); } elseif ($action == 'email') { if (Auth::user()->confirmed && !Auth::user()->isDemo()) { - $response = $this->mailer->sendInvoice($invoice); + if ($invoice->is_recurring) { + if ($invoice->shouldSendToday()) { + $invoice = $this->invoiceRepo->createRecurringInvoice($invoice); + $response = $this->mailer->sendInvoice($invoice); + } else { + $response = trans('texts.recurring_too_soon'); + } + } else { + $response = $this->mailer->sendInvoice($invoice); + } if ($response === true) { $message = trans("texts.emailed_{$entityType}"); Session::flash('message', $message); diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index bb8526c424e9..589841a314bf 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -158,7 +158,8 @@ class QuoteController extends BaseController 'paymentTerms' => Cache::get('paymentTerms'), 'industries' => Cache::get('industries'), 'invoiceDesigns' => InvoiceDesign::getDesigns(), - 'invoiceLabels' => Auth::user()->account->getInvoiceLabels() + 'invoiceLabels' => Auth::user()->account->getInvoiceLabels(), + 'isRecurring' => false, ]; } diff --git a/app/Http/routes.php b/app/Http/routes.php index 706ddbd3bb9e..7c158a314490 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -1,6 +1,5 @@ 'auth'], function() { Route::get('tasks/create/{client_id?}', 'TaskController@create'); Route::post('tasks/bulk', 'TaskController@bulk'); - Route::get('recurring_invoices', 'InvoiceController@recurringIndex'); Route::get('api/recurring_invoices/{client_id?}', array('as'=>'api.recurring_invoices', 'uses'=>'InvoiceController@getRecurringDatatable')); Route::get('invoices/invoice_history/{invoice_id}', 'InvoiceController@invoiceHistory'); @@ -141,6 +139,7 @@ Route::group(['middleware' => 'auth'], function() { Route::resource('invoices', 'InvoiceController'); Route::get('api/invoices/{client_id?}', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable')); Route::get('invoices/create/{client_id?}', 'InvoiceController@create'); + Route::get('recurring_invoices/create/{client_id?}', 'InvoiceController@createRecurring'); Route::get('invoices/{public_id}/clone', 'InvoiceController@cloneInvoice'); Route::post('invoices/bulk', 'InvoiceController@bulk'); diff --git a/app/Models/Client.php b/app/Models/Client.php index a9b6c8fe95d3..554f74556910 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -83,10 +83,6 @@ class Client extends EntityModel return $this->name; } - if (!$this->contacts || !count($this->contacts)) { - $this->load('contacts'); - } - $contact = $this->contacts()->first(); return $contact->getDisplayName(); diff --git a/app/Models/EntityModel.php b/app/Models/EntityModel.php index bf44b6f6d886..550de1d3cef0 100644 --- a/app/Models/EntityModel.php +++ b/app/Models/EntityModel.php @@ -44,7 +44,7 @@ class EntityModel extends Eloquent public function getActivityKey() { - return '[' . $this->getEntityType().':'.$this->public_id.':'.$this->getName() . ']'; + return '[' . $this->getEntityType().':'.$this->public_id.':'.$this->getDisplayName() . ']'; } /* @@ -83,6 +83,11 @@ class EntityModel extends Eloquent return $this->public_id; } + public function getDisplayName() + { + return $this->getName(); + } + // Remap ids to public_ids and show name public function toPublicArray() { diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 63700410fa18..aeebfaed6ab0 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -48,6 +48,11 @@ class Invoice extends EntityModel return $this->belongsTo('App\Models\Invoice'); } + public function recurring_invoices() + { + return $this->hasMany('App\Models\Invoice', 'recurring_invoice_id'); + } + public function invitations() { return $this->hasMany('App\Models\Invitation')->orderBy('invitations.contact_id'); @@ -55,7 +60,7 @@ class Invoice extends EntityModel public function getName() { - return $this->invoice_number; + return $this->is_recurring ? trans('texts.recurring') : $this->invoice_number; } public function getFileName() @@ -258,7 +263,9 @@ class Invoice extends EntityModel } Invoice::creating(function ($invoice) { - $invoice->account->incrementCounter($invoice->is_quote); + if (!$invoice->is_recurring) { + $invoice->account->incrementCounter($invoice->is_quote); + } }); Invoice::created(function ($invoice) { diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index e93ea47826bb..bfbf62658f45 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -1,11 +1,12 @@ select(['public_id', 'invoice_number']) ->get(); } + + public function createRecurringInvoice($recurInvoice) + { + $recurInvoice->load('account.timezone', 'invoice_items', 'client', 'user'); + + if ($recurInvoice->client->deleted_at) { + return false; + } + + if (!$recurInvoice->user->confirmed) { + return false; + } + + if (!$recurInvoice->shouldSendToday()) { + return false; + } + + $invoice = Invoice::createNew($recurInvoice); + $invoice->client_id = $recurInvoice->client_id; + $invoice->recurring_invoice_id = $recurInvoice->id; + $invoice->invoice_number = $recurInvoice->account->getNextInvoiceNumber(false, 'R'); + $invoice->amount = $recurInvoice->amount; + $invoice->balance = $recurInvoice->amount; + $invoice->invoice_date = date_create()->format('Y-m-d'); + $invoice->discount = $recurInvoice->discount; + $invoice->po_number = $recurInvoice->po_number; + $invoice->public_notes = Utils::processVariables($recurInvoice->public_notes); + $invoice->terms = Utils::processVariables($recurInvoice->terms); + $invoice->invoice_footer = Utils::processVariables($recurInvoice->invoice_footer); + $invoice->tax_name = $recurInvoice->tax_name; + $invoice->tax_rate = $recurInvoice->tax_rate; + $invoice->invoice_design_id = $recurInvoice->invoice_design_id; + $invoice->custom_value1 = $recurInvoice->custom_value1; + $invoice->custom_value2 = $recurInvoice->custom_value2; + $invoice->custom_taxes1 = $recurInvoice->custom_taxes1; + $invoice->custom_taxes2 = $recurInvoice->custom_taxes2; + $invoice->is_amount_discount = $recurInvoice->is_amount_discount; + + if ($invoice->client->payment_terms != 0) { + $days = $invoice->client->payment_terms; + if ($days == -1) { + $days = 0; + } + $invoice->due_date = date_create()->modify($days.' day')->format('Y-m-d'); + } + + $invoice->save(); + + foreach ($recurInvoice->invoice_items as $recurItem) { + $item = InvoiceItem::createNew($recurItem); + $item->product_id = $recurItem->product_id; + $item->qty = $recurItem->qty; + $item->cost = $recurItem->cost; + $item->notes = Utils::processVariables($recurItem->notes); + $item->product_key = Utils::processVariables($recurItem->product_key); + $item->tax_name = $recurItem->tax_name; + $item->tax_rate = $recurItem->tax_rate; + $invoice->invoice_items()->save($item); + } + + foreach ($recurInvoice->invitations as $recurInvitation) { + $invitation = Invitation::createNew($recurInvitation); + $invitation->contact_id = $recurInvitation->contact_id; + $invitation->invitation_key = str_random(RANDOM_KEY_LENGTH); + $invoice->invitations()->save($invitation); + } + + $recurInvoice->last_sent_date = Carbon::now()->toDateTimeString(); + $recurInvoice->save(); + + return $invoice; + } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 9efba90b91af..d9fc74798f69 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -40,10 +40,13 @@ class AppServiceProvider extends ServiceProvider {