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 eb9be3baae..d2ba5375c3 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 ad2262124d..05af271289 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 1c12d36aa4..7a76d1c22a 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 2976327b5d..bb68353b70 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 3a0aaa975b..957b6bff8e 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 9d6d07c489..d0b5630815 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 e65ebae5d7..6aada81e4a 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 d0b5630815..e70592d63e 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 b1746761b1..50a529a701 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 94025347fc..3ac30c47b5 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 010c555770..8ded07fc32 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 3e808ab19e..71b75ac797 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 3e358a158c..4de932ca45 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 ba3b95aa1b..05b302aede 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 79de433a6b..59a486405d 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 540567d9b3..dcb9438c1a 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 b6f3c5df22..ff4c84c649 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 0a9220c727..20c938e825 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 a68841d580..6248a8e691 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 a1b12dad29..9ca05f64f5 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 0825afb383..542c2638e7 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 6207bfb140..27c0999c7a 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 68cfaffe2e..4ccbf9688a 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 47c7756ab5..ffd0abdade 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 f4caa63ee2..ce0397d64b 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 feb764850a..4504e78fb7 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 0000000000..b0136a33ec --- /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 5c4e52f5c5..ab4839c61f 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 5e53681075..6c31f30419 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 706250b733..5c9b160aa6 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 461b878114..482c29ef0b 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 728fd8c376..7861fb9a80 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 0d32d825ee..826902066b 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 38e48e8276..e230e930f1 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 010c555770..e16f140f5e 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 3e808ab19e..724cf87a43 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 8b5a3e5047..4892d732c3 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 66f56de588..eaf8bbb9e5 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 a5d9d569da..1e2a21df14 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 6dad49264b..f4e64362a2 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 be2080b683..bfa9c57fa9 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 86a9de4b84..e26f1ad515 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 13d6cd337e..2aa79c2dbb 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 feef5eceb5..90475d384b 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 b46bdfd17b..b3b0301157 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 f4fc371e66..857cbb6b06 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 e016e3841c..dc58f5d14e 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 a6c2741977..a0b7fd2b1d 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 dc58f5d14e..a9c322632d 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 6632ce843b..712f19dce6 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 59a486405d..b9dec31654 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 e17135fe71..a5897ac687 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 50b66b69f7..85b4cede7d 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 b389113714..1efcf4b229 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 be307f6ca7..e8cde5293c 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 98ef973f67..222463c4b5 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 0a22d2a5a3..a47d137523 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 dded462d10..eabb1be9f6 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 89a2a179f9..b433f55b73 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 e42e71f275..9abb876907 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 317b729653..db574f26b3 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 7364f33f6a..9384f621b4 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 b135c04505..bc6c699e72 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 222463c4b5..5aaa6e8d4d 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 2645b0cbd9..0f8226f077 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 580e366e72..ced5504988 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 7519119b40..9cd8866c92 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 b9807c4c93..23b684fb25 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 92ca314723..cf29027761 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 97b333fbb2..8d6fd83a7f 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 6fd60e17c4..6a87a6e788 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 0c0bf2e3a4..05d56c5782 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 9672b9ea53..061bd060be 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 15ca60fcba..ba4f8715e5 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 c014c91763..eeb80190ad 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 f82175f36a..aa8458c590 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 8756914127..8bb24e7328 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 13e443f81b..09aaa79987 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 864e7aeb1f..f4e52f16e6 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 cc9d3e35ce..915c176f8c 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 a9c322632d..706ddbd3bb 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 1efcf4b229..1263b4ed67 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 e8cde5293c..ee88e2fd1f 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 5aaa6e8d4d..d33c08f7ab 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 a47d137523..08fa79b130 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 168a873356..8ec21c1e5b 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 eabb1be9f6..221b8d0a0d 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 23b684fb25..1a159cb2c5 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 9abb876907..6925992c31 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 9384f621b4..4848a190e5 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 75fa8a500e..3839a0bb43 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 8f65214f7f..8f31473c9e 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 248b1be6be..efed3effd1 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 bb8526c424..589841a314 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 706ddbd3bb..7c158a3144 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 a9b6c8fe95..554f745569 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 bf44b6f6d8..550de1d3ce 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 63700410fa..aeebfaed6a 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 e93ea47826..bfbf62658f 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 9efba90b91..d9fc74798f 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -40,10 +40,13 @@ class AppServiceProvider extends ServiceProvider {