From 2d26c05fc40be2158955e5dc8d59d1d892b1a062 Mon Sep 17 00:00:00 2001 From: b1ake Date: Fri, 6 Jun 2014 19:30:37 +0000 Subject: [PATCH 01/45] First commit. Basic API data is being written to the answer bar. --- lib/DDG/Spice/Domains.pm | 22 +++++++++++++ share/spice/domains/available.handlebars | 2 ++ share/spice/domains/domains.js | 39 ++++++++++++++++++++++++ share/spice/domains/whois.handlebars | 2 ++ 4 files changed, 65 insertions(+) create mode 100644 lib/DDG/Spice/Domains.pm create mode 100644 share/spice/domains/available.handlebars create mode 100644 share/spice/domains/domains.js create mode 100644 share/spice/domains/whois.handlebars diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Domains.pm new file mode 100644 index 0000000000..17d14b4efa --- /dev/null +++ b/lib/DDG/Spice/Domains.pm @@ -0,0 +1,22 @@ +package DDG::Spice::Domains; +# ABSTRACT: Returns an internet domain's availability and whois information. + +use DDG::Spice; + +triggers startend => + 'whois', + 'domain availabile', + 'buy domain'; + +my $api_user = 'USERNAME_HERE'; +my $api_pwd = 'PWD_HERE'; +spice to => 'https://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username=' . $api_user . '&password=' . $api_pwd; + +handle remainder => sub { + my ($domain) = @_; + return if !$domain; # do not trigger AI if domain is blank + + return $domain; +}; + +1; \ No newline at end of file diff --git a/share/spice/domains/available.handlebars b/share/spice/domains/available.handlebars new file mode 100644 index 0000000000..0141e7c98d --- /dev/null +++ b/share/spice/domains/available.handlebars @@ -0,0 +1,2 @@ +
The domain "{{domainName}}" is probably available!
+
Buy it now
\ No newline at end of file diff --git a/share/spice/domains/domains.js b/share/spice/domains/domains.js new file mode 100644 index 0000000000..89e8402d11 --- /dev/null +++ b/share/spice/domains/domains.js @@ -0,0 +1,39 @@ +(function (env) { + "use strict"; + + env.ddg_spice_domains = function(api_result) { + if (api_result.error) { + return Spice.failed('npm'); + } + + // get the relevant part of the result + api_result = api_result.WhoisRecord; + + // for debugging + console.log(api_result); + + var is_availble = !!api_result.registrant; + var template_to_use = is_availble ? Spice.domains.available : Spice.domains.whois; + + + Spice.add({ + id: "domains", + name: "Domains", + data: api_result, + meta: { + sourceName: "WhoisAPI", + sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' + api_result.domainName + '&outputFormat=json' + + }, + templates: { + group: 'base', + options:{ + content: template_to_use, + moreAt: true + } + } + }); + + }; + +}(this)); \ No newline at end of file diff --git a/share/spice/domains/whois.handlebars b/share/spice/domains/whois.handlebars new file mode 100644 index 0000000000..c5fa792408 --- /dev/null +++ b/share/spice/domains/whois.handlebars @@ -0,0 +1,2 @@ +
{{domainName}}
+
{{contactEmail}}
\ No newline at end of file From a19df5fa00721ac873b5417d0b637709dbb35b02 Mon Sep 17 00:00:00 2001 From: b1ake Date: Mon, 16 Jun 2014 21:24:53 +0000 Subject: [PATCH 02/45] Trigger when query contains only a URL. --- lib/DDG/Spice/Domains.pm | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Domains.pm index 17d14b4efa..a51807d21e 100644 --- a/lib/DDG/Spice/Domains.pm +++ b/lib/DDG/Spice/Domains.pm @@ -3,20 +3,35 @@ package DDG::Spice::Domains; use DDG::Spice; -triggers startend => - 'whois', - 'domain availabile', - 'buy domain'; +my $tlds_qr = qr/com|co|org|ac|ac.uk|in/; +my $url_qr = qr/^(?:http:\/\/)?(?:www\.)?(.*?)\.($tlds_qr)$/; + +# trigger when query contains only a URL +triggers query_raw => $url_qr; + +# TODO: add other triggers into the qr above, such as 'whois', 'domain', 'available', 'is available' my $api_user = 'USERNAME_HERE'; my $api_pwd = 'PWD_HERE'; spice to => 'https://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username=' . $api_user . '&password=' . $api_pwd; -handle remainder => sub { - my ($domain) = @_; - return if !$domain; # do not trigger AI if domain is blank +handle sub { + my ($query) = @_; + return if !$query; # do not trigger this spice if the query is blank + + # parse the URL into its parts + my ($domain, $tld) = $query =~ $url_qr; - return $domain; + warn $domain . '', "\t", $tld . ''; + + # skip if no domain or tld + return if !defined $domain || $domain eq '' || !defined $tld || $tld eq ''; + + # skip if domain contains a subdomain + return if $domain =~ /\./; + + # return the domain + the tld, with a period in between + return "$domain.$tld"; }; -1; \ No newline at end of file +1; From c6a8d945affeb7cc91eb0513fd0d18598e844e83 Mon Sep 17 00:00:00 2001 From: b1ake Date: Tue, 17 Jun 2014 21:47:24 +0000 Subject: [PATCH 03/45] Rough working version. Includes complex triggers and selective showing of whois info. More detail on the commit: - This spice is now triggered by either a) naked domains (i.e. query containing only a domain), or b) queries that contain a domain as well as certain keywords at the beginning or end of the string. - The handle() function now correctly parses the domain, even if other keywords are in the query. - The API username and password are now handled by environment vars, rather than hard-coded. - If a domain is available, this fact is always shown to the user. - If a domain is not available, whois info for that domain is shown only when whois-related keywords are present. Whois info is NOT shows for naked domains, because a user who types 'google.com' probably doesn't want to see whois info (but may want to see a message when that domain happens to be available). - More whois information is now shown. This required unnesting some fields within the API output and doing some basic formatting on date strings. - Encapsulated more code into function. --- lib/DDG/Spice/Domains.pm | 20 +++-- share/spice/domains/available.handlebars | 5 +- share/spice/domains/domains.js | 101 +++++++++++++++++++---- share/spice/domains/whois.handlebars | 9 +- 4 files changed, 105 insertions(+), 30 deletions(-) diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Domains.pm index a51807d21e..d28bfa9894 100644 --- a/lib/DDG/Spice/Domains.pm +++ b/lib/DDG/Spice/Domains.pm @@ -3,17 +3,21 @@ package DDG::Spice::Domains; use DDG::Spice; +# regexes for parsing URLs my $tlds_qr = qr/com|co|org|ac|ac.uk|in/; -my $url_qr = qr/^(?:http:\/\/)?(?:www\.)?(.*?)\.($tlds_qr)$/; +my $url_qr = qr/(?:http:\/\/)?(?:www\.)?([^\s]*?)\.($tlds_qr)/; -# trigger when query contains only a URL -triggers query_raw => $url_qr; +# additional keywords that trigger this spice +my $whois_keywords_qr = qr/whois|lookup|(?:is\s|)domain|(?:is\s|)available|register|owner(?:\sof|)|who\sowns/i; -# TODO: add other triggers into the qr above, such as 'whois', 'domain', 'available', 'is available' +# trigger this spice when: +# - query contains only a URL +# - query starts or end with any of the whois keywords +triggers query_raw => + qr/^$url_qr$/, + qr/^$whois_keywords_qr|$whois_keywords_qr$/; -my $api_user = 'USERNAME_HERE'; -my $api_pwd = 'PWD_HERE'; -spice to => 'https://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username=' . $api_user . '&password=' . $api_pwd; +spice to => 'https://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username={{ENV{DDG_SPICE_DOMAINS_USERNAME}}}&password={{ENV{DDG_SPICE_DOMAINS_PASSWORD}}}'; handle sub { my ($query) = @_; @@ -22,7 +26,7 @@ handle sub { # parse the URL into its parts my ($domain, $tld) = $query =~ $url_qr; - warn $domain . '', "\t", $tld . ''; + warn $domain || '', "\t", $tld || ''; # skip if no domain or tld return if !defined $domain || $domain eq '' || !defined $tld || $tld eq ''; diff --git a/share/spice/domains/available.handlebars b/share/spice/domains/available.handlebars index 0141e7c98d..d99daeba85 100644 --- a/share/spice/domains/available.handlebars +++ b/share/spice/domains/available.handlebars @@ -1,2 +1,3 @@ -
The domain "{{domainName}}" is probably available!
-
Buy it now
\ No newline at end of file +
The domain {{domainName}} is probably available!
+
+
Register it at ______ [affiliate link goes here]
\ No newline at end of file diff --git a/share/spice/domains/domains.js b/share/spice/domains/domains.js index 89e8402d11..6c23a7208a 100644 --- a/share/spice/domains/domains.js +++ b/share/spice/domains/domains.js @@ -1,39 +1,104 @@ (function (env) { - "use strict"; - + "use strict"; + + // flag for debugging output + var is_debug = true; + env.ddg_spice_domains = function(api_result) { - if (api_result.error) { - return Spice.failed('npm'); + // for debugging + if(is_debug) console.log('api_result:', api_result); + + if (!api_result || api_result.error || !api_result.WhoisRecord) { + return Spice.failed('domains'); } // get the relevant part of the result api_result = api_result.WhoisRecord; - // for debugging - console.log(api_result); - - var is_availble = !!api_result.registrant; - var template_to_use = is_availble ? Spice.domains.available : Spice.domains.whois; - + // is the domain available? + var is_avail = is_domain_available(api_result); + + // if the domain isn't available, do we want to show + // whois information? + var is_whois_allowed = is_whois_query(DDG.get_query()); + + // for debugging + if(is_debug) console.log("is_avail:", is_avail, "is_whois_allowed:", is_whois_allowed); + + // decide which template to show, if any + if(is_avail) { + // always show result when domain is available + show_result(api_result, Spice.domains.available); + } else if(is_whois_allowed) { + // if domain is not available, show whois only for designated 'whois' queries + show_result(api_result, Spice.domains.whois); + } else { + // by default, show nothing + } + + }; - Spice.add({ + // Returns whether the domain is available, + // based on the API result that was returned. + var is_domain_available = function(api_result) { + + // no registrant means that a domain is available + // + // TODO: Figure out cases when domain is available but there + // is already a registrant, such as with expired domains. + return !api_result.registrant; + }; + + // Returns whether we should show whois data if this + // domain is not available. + var is_whois_query = function(query) { + + // for debugging + if(is_debug) console.log('in is_whois_query, query =', query); + + // show whois results except when the query contains only the domain + // and no other keywords, which we test by looking for spaces in the query. + return /\s/.test(query); + }; + + // Show the result using the template specified. + var show_result = function(api_result, template) { + + // bring data we need to top level of the object + api_result = { + domainName: api_result.domainName, + registrantName: api_result.registrant && api_result.registrant.name, + contactEmail: api_result.contactEmail, + updatedDate: api_result.updatedDate, + expirationDate: api_result.expiresDate, + }; + + // trim times from end of date strings + api_result.updatedDate = api_result.updatedDate && api_result.updatedDate.replace(/^(.*)?T(.*)?$/, '$1'); + api_result.expirationDate = api_result.expirationDate && api_result.expirationDate.replace(/^(.*)?T(.*)?$/, '$1'); + + Spice.add({ id: "domains", name: "Domains", data: api_result, meta: { sourceName: "WhoisAPI", - sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' + api_result.domainName + '&outputFormat=json' + sourceUrl: + 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' + + api_result.domainName + + '&outputFormat=json' }, templates: { group: 'base', - options:{ - content: template_to_use, - moreAt: true - } - } + options:{ + content: template, + moreAt: true + } + } }); - }; + + }(this)); \ No newline at end of file diff --git a/share/spice/domains/whois.handlebars b/share/spice/domains/whois.handlebars index c5fa792408..824f42bf5d 100644 --- a/share/spice/domains/whois.handlebars +++ b/share/spice/domains/whois.handlebars @@ -1,2 +1,7 @@ -
{{domainName}}
-
{{contactEmail}}
\ No newline at end of file +
Whois for {{domainName}}
+
+
Registered to: {{registrantName}}
+
Email: {{contactEmail}}
+
Last updated: {{updatedDate}}
+
Expires: {{expirationDate}}
+ From 21cfe989a509738f8d316af07f25a1d6f346a304 Mon Sep 17 00:00:00 2001 From: b1ake Date: Wed, 18 Jun 2014 17:10:26 +0000 Subject: [PATCH 04/45] Added tests. --- lib/DDG/Spice/Domains.pm | 4 +- t/Domains.t | 106 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 t/Domains.t diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Domains.pm index d28bfa9894..df2b70c98f 100644 --- a/lib/DDG/Spice/Domains.pm +++ b/lib/DDG/Spice/Domains.pm @@ -8,7 +8,7 @@ my $tlds_qr = qr/com|co|org|ac|ac.uk|in/; my $url_qr = qr/(?:http:\/\/)?(?:www\.)?([^\s]*?)\.($tlds_qr)/; # additional keywords that trigger this spice -my $whois_keywords_qr = qr/whois|lookup|(?:is\s|)domain|(?:is\s|)available|register|owner(?:\sof|)|who\sowns/i; +my $whois_keywords_qr = qr/whois|lookup|(?:is\s|)domain|(?:is\s|)available|register|owner(?:\sof|)|who\sowns|(?:how\sto\s|)buy/i; # trigger this spice when: # - query contains only a URL @@ -26,8 +26,6 @@ handle sub { # parse the URL into its parts my ($domain, $tld) = $query =~ $url_qr; - warn $domain || '', "\t", $tld || ''; - # skip if no domain or tld return if !defined $domain || $domain eq '' || !defined $tld || $tld eq ''; diff --git a/t/Domains.t b/t/Domains.t new file mode 100644 index 0000000000..1e61df6b72 --- /dev/null +++ b/t/Domains.t @@ -0,0 +1,106 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Test::More; +use DDG::Test::Spice; + +ddg_spice_test( + + # This is the name of the Spice that will be loaded to test. + [ 'DDG::Spice::Domains' ], + + # A naked domain should trigger. + 'duckduckgo.com' => expected_output_for('duckduckgo.com'), + + # A naked domain with 'www' should trigger. + 'http://www.duckduckgo.com' => expected_output_for('duckduckgo.com'), + + # A naked domain with a subdomain that's not 'www' should not trigger. + 'blah.duckduckgo.com' => undef, + + # A naked domain with an unknown tld should not trigger. + 'blah.duckduckgo.wtflmao' => undef, + + # A naked domain with a port should not trigger. + 'duckduckgo.com:8000' => undef, + + # A naked domain with a resource path should not trigger. + 'duckduckgo.com/about' => undef, + + # A domain preceeded by 'http://' should trigger. + 'http://duckduckgo.com' => expected_output_for('duckduckgo.com'), + + # A domain preceeded by 'http://' and 'www' should trigger. + 'http://www.duckduckgo.com' => expected_output_for('duckduckgo.com'), + + # A domain preceeded by 'http://' and a subdomain that's not 'www' should not trigger. + 'http://blah.duckduckgo.com' => undef, + + # A random keyword before the domain should not trigger + 'blah duckduckgo.com' => undef, + + # A random keyword after the domain should not trigger + 'duckduckgo.com blah' => undef, + + # whois keywords before a url should trigger + 'whois duckduckgo.com' => expected_output_for('duckduckgo.com'), + 'lookup duckduckgo.com' => expected_output_for('duckduckgo.com'), + 'domain duckduckgo.com' => expected_output_for('duckduckgo.com'), + 'is domain duckduckgo.com' => expected_output_for('duckduckgo.com'), + 'available duckduckgo.com' => expected_output_for('duckduckgo.com'), + 'is available duckduckgo.com' => expected_output_for('duckduckgo.com'), + 'register duckduckgo.com' => expected_output_for('duckduckgo.com'), + 'owner duckduckgo.com' => expected_output_for('duckduckgo.com'), + 'owner of duckduckgo.com' => expected_output_for('duckduckgo.com'), + 'who owns duckduckgo.com' => expected_output_for('duckduckgo.com'), + 'buy duckduckgo.com' => expected_output_for('duckduckgo.com'), + 'how to buy duckduckgo.com' => expected_output_for('duckduckgo.com'), + + # whois keywords after a url should trigger + 'duckduckgo.com whois' => expected_output_for('duckduckgo.com'), + 'duckduckgo.com lookup' => expected_output_for('duckduckgo.com'), + 'duckduckgo.com domain' => expected_output_for('duckduckgo.com'), + 'duckduckgo.com is domain' => expected_output_for('duckduckgo.com'), + 'duckduckgo.com available' => expected_output_for('duckduckgo.com'), + 'duckduckgo.com is available' => expected_output_for('duckduckgo.com'), + 'duckduckgo.com register' => expected_output_for('duckduckgo.com'), + 'duckduckgo.com owner' => expected_output_for('duckduckgo.com'), + 'duckduckgo.com who owns' => expected_output_for('duckduckgo.com'), + 'duckduckgo.com buy' => expected_output_for('duckduckgo.com'), + 'duckduckgo.com how to buy' => expected_output_for('duckduckgo.com'), + + # whois keywords without a url should not trigger + 'whois' => undef, + 'lookup' => undef, + 'domain' => undef, + 'is domain' => undef, + 'available' => undef, + 'is available' => undef, + 'register' => undef, + 'owner' => undef, + 'owner' => undef, + 'who owns' => undef, + 'buy' => undef, + 'how to buy' => undef, + +); + +# Returns the output we expect to receive from the test +# when the domain spice is triggered properly. +sub expected_output_for { + + # get the domain we expect for the spice trigger + my ($domain_expected) = @_; + return undef if !defined $domain_expected; + + # return the output we expect for the spice test + return test_spice( + '/js/spice/domains/' . $domain_expected, + call_type => 'include', + caller => 'DDG::Spice::Domains', + ); +} + +done_testing; From dc6819c1ed92b39b9aac9498494a1ca521ce2cfc Mon Sep 17 00:00:00 2001 From: b1ake Date: Wed, 18 Jun 2014 17:15:26 +0000 Subject: [PATCH 05/45] Added regex for TLDs from main DDG codebase. --- lib/DDG/Spice/Domains.pm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Domains.pm index df2b70c98f..1e74354b61 100644 --- a/lib/DDG/Spice/Domains.pm +++ b/lib/DDG/Spice/Domains.pm @@ -3,8 +3,10 @@ package DDG::Spice::Domains; use DDG::Spice; -# regexes for parsing URLs -my $tlds_qr = qr/com|co|org|ac|ac.uk|in/; +# regex for allowed TLDS (grabbed from main DDG code, in /lib/DDG/Util/Constants.pm) +my $tlds_qr = qr/(?:c(?:o(?:m|op)?|at?|[iykgdmnxruhcfzvl])|o(?:rg|m)|n(?:et?|a(?:me)?|[ucgozrfpil])|e(?:d?u|[gechstr])|i(?:n(?:t|fo)?|[stqldroem])|m(?:o(?:bi)?|u(?:seum)?|i?l|[mcyvtsqhaerngxzfpwkd])|g(?:ov|[glqeriabtshdfmuywnp])|b(?:iz?|[drovfhtaywmzjsgbenl])|t(?:r(?:avel)?|[ncmfzdvkopthjwg]|e?l)|k[iemygznhwrp]|s[jtvberindlucygkhaozm]|u[gymszka]|h[nmutkr]|r[owesu]|d[kmzoej]|a(?:e(?:ro)?|r(?:pa)?|[qofiumsgzlwcnxdt])|p(?:ro?|[sgnthfymakwle])|v[aegiucn]|l[sayuvikcbrt]|j(?:o(?:bs)?|[mep])|w[fs]|z[amw]|f[rijkom]|y[eut]|qa)/i; + +# regex for parsing URLs my $url_qr = qr/(?:http:\/\/)?(?:www\.)?([^\s]*?)\.($tlds_qr)/; # additional keywords that trigger this spice From c99fda3f246ef67e2a672ddef99e86740f77cff4 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 19 Jun 2014 12:01:32 +0000 Subject: [PATCH 06/45] Lowercased domains + added tests. Also added tests for whois keywords being case-sensitive. --- lib/DDG/Spice/Domains.pm | 6 ++++-- t/Domains.t | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Domains.pm index 1e74354b61..c774caa266 100644 --- a/lib/DDG/Spice/Domains.pm +++ b/lib/DDG/Spice/Domains.pm @@ -1,5 +1,7 @@ package DDG::Spice::Domains; -# ABSTRACT: Returns an internet domain's availability and whois information. + +# ABSTRACT: +# Returns an internet domain's availability and whois information. use DDG::Spice; @@ -35,7 +37,7 @@ handle sub { return if $domain =~ /\./; # return the domain + the tld, with a period in between - return "$domain.$tld"; + return lc "$domain.$tld"; }; 1; diff --git a/t/Domains.t b/t/Domains.t index 1e61df6b72..ae519a552d 100644 --- a/t/Domains.t +++ b/t/Domains.t @@ -17,6 +17,9 @@ ddg_spice_test( # A naked domain with 'www' should trigger. 'http://www.duckduckgo.com' => expected_output_for('duckduckgo.com'), + # Domains should be lowercased + 'dUcKdUcKgO.cOm' => expected_output_for('duckduckgo.com'), + # A naked domain with a subdomain that's not 'www' should not trigger. 'blah.duckduckgo.com' => undef, @@ -58,6 +61,9 @@ ddg_spice_test( 'buy duckduckgo.com' => expected_output_for('duckduckgo.com'), 'how to buy duckduckgo.com' => expected_output_for('duckduckgo.com'), + # whois keywords should be case insensitive + 'hOw To bUy duckduckgo.com' => expected_output_for('duckduckgo.com'), + # whois keywords after a url should trigger 'duckduckgo.com whois' => expected_output_for('duckduckgo.com'), 'duckduckgo.com lookup' => expected_output_for('duckduckgo.com'), From c9a62f7ed2e515f1e9c986beb139f440b3d329b0 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 19 Jun 2014 12:38:49 +0000 Subject: [PATCH 07/45] Triggering tweaks in progress (not finished, and not all tests passing). --- lib/DDG/Spice/Domains.pm | 30 +++++++++++++++++++++++++----- t/Domains.t | 12 ++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Domains.pm index c774caa266..75c1d91a0c 100644 --- a/lib/DDG/Spice/Domains.pm +++ b/lib/DDG/Spice/Domains.pm @@ -9,7 +9,7 @@ use DDG::Spice; my $tlds_qr = qr/(?:c(?:o(?:m|op)?|at?|[iykgdmnxruhcfzvl])|o(?:rg|m)|n(?:et?|a(?:me)?|[ucgozrfpil])|e(?:d?u|[gechstr])|i(?:n(?:t|fo)?|[stqldroem])|m(?:o(?:bi)?|u(?:seum)?|i?l|[mcyvtsqhaerngxzfpwkd])|g(?:ov|[glqeriabtshdfmuywnp])|b(?:iz?|[drovfhtaywmzjsgbenl])|t(?:r(?:avel)?|[ncmfzdvkopthjwg]|e?l)|k[iemygznhwrp]|s[jtvberindlucygkhaozm]|u[gymszka]|h[nmutkr]|r[owesu]|d[kmzoej]|a(?:e(?:ro)?|r(?:pa)?|[qofiumsgzlwcnxdt])|p(?:ro?|[sgnthfymakwle])|v[aegiucn]|l[sayuvikcbrt]|j(?:o(?:bs)?|[mep])|w[fs]|z[amw]|f[rijkom]|y[eut]|qa)/i; # regex for parsing URLs -my $url_qr = qr/(?:http:\/\/)?(?:www\.)?([^\s]*?)\.($tlds_qr)/; +my $url_qr = qr/(?:http:\/\/)?(?:www\.)?([^\s]*?)\.($tlds_qr)(\:?[0-9]{1,4})?([^\s]*)/; # additional keywords that trigger this spice my $whois_keywords_qr = qr/whois|lookup|(?:is\s|)domain|(?:is\s|)available|register|owner(?:\sof|)|who\sowns|(?:how\sto\s|)buy/i; @@ -28,16 +28,36 @@ handle sub { return if !$query; # do not trigger this spice if the query is blank # parse the URL into its parts - my ($domain, $tld) = $query =~ $url_qr; - + my ($domain, $tld, $port, $resource_path) = $query =~ $url_qr; + + warn $query, "\t", $domain || '', "\t", $tld || '', "\t", $port || '', "\t", $resource_path || ''; + + # get the non-URL text from the query by combining the text before and after the match + my $non_url_text = $` . $'; #' <-- close tick for syntax highlighting + + # is the string a naked domain, i.e. is there any text besides the domain + my $is_naked_domain = trim($non_url_text) ne ''; + # skip if no domain or tld return if !defined $domain || $domain eq '' || !defined $tld || $tld eq ''; - # skip if domain contains a subdomain - return if $domain =~ /\./; + # skip if naked domain that contains a subdomain, port or resource_path + # (notice that we allow subdomains, ports, and resource paths when + # there is a whois keyword) + return if $is_naked_domain + && ($domain =~ /\./ + || (defined $port && $port ne '') + || (defined $resource_path && $resource_path ne '')); # return the domain + the tld, with a period in between return lc "$domain.$tld"; }; +# Returns a string with leading and trailing spaces removed. +sub trim { + my ($str) = @_; + $str =~ s/^\s*(.*)?\s*$/$1/; + return $str; +} + 1; diff --git a/t/Domains.t b/t/Domains.t index ae519a552d..d9e9f41fec 100644 --- a/t/Domains.t +++ b/t/Domains.t @@ -23,15 +23,27 @@ ddg_spice_test( # A naked domain with a subdomain that's not 'www' should not trigger. 'blah.duckduckgo.com' => undef, + # Whois keywords with a subdomain that's not 'www' should trigger. + 'whois blah.duckduckgo.com' => expected_output_for('duckduckgo.com'), + # A naked domain with an unknown tld should not trigger. 'blah.duckduckgo.wtflmao' => undef, + # Whois keywords with an unknown tld should not trigger. + 'whois blah.duckduckgo.wtflmao' => undef, + # A naked domain with a port should not trigger. 'duckduckgo.com:8000' => undef, + # Whois keywords with a port should trigger. + 'whois duckduckgo.com:8000' => expected_output_for('duckduckgo.com'), + # A naked domain with a resource path should not trigger. 'duckduckgo.com/about' => undef, + # Whois keywords with a resource path should trigger. + 'whoisduckduckgo.com/about' => expected_output_for('duckduckgo.com'), + # A domain preceeded by 'http://' should trigger. 'http://duckduckgo.com' => expected_output_for('duckduckgo.com'), From 158c2fa335096f93172cc2d34ca087af6210d6c6 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 19 Jun 2014 12:56:17 +0000 Subject: [PATCH 08/45] Now using the 'record' template. --- share/spice/domains/domains.js | 59 ++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/share/spice/domains/domains.js b/share/spice/domains/domains.js index 6c23a7208a..29d2503832 100644 --- a/share/spice/domains/domains.js +++ b/share/spice/domains/domains.js @@ -27,11 +27,11 @@ // decide which template to show, if any if(is_avail) { - // always show result when domain is available - show_result(api_result, Spice.domains.available); + // show message saying the domain is available + show_available(api_result); } else if(is_whois_allowed) { - // if domain is not available, show whois only for designated 'whois' queries - show_result(api_result, Spice.domains.whois); + // show whois info for the domain + show_whois(api_result); } else { // by default, show nothing } @@ -58,29 +58,48 @@ // show whois results except when the query contains only the domain // and no other keywords, which we test by looking for spaces in the query. - return /\s/.test(query); + return /\s/.test(query.trim()); }; - // Show the result using the template specified. - var show_result = function(api_result, template) { + // Show message saying that the domain is available. + var show_available = function(api_result) { + Spice.add({ + id: "domains", + name: "Domains", + data: api_result, + meta: { + sourceName: "WhoisAPI", + sourceUrl: + 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' + + api_result.domainName + + '&outputFormat=json' + }, + templates: { + group: 'base', + options:{ + content: Spice.domains.available, + moreAt: true + } + } + }); + }; - // bring data we need to top level of the object - api_result = { - domainName: api_result.domainName, - registrantName: api_result.registrant && api_result.registrant.name, - contactEmail: api_result.contactEmail, - updatedDate: api_result.updatedDate, - expirationDate: api_result.expiresDate, + // Show whois info for the domain using the 'record' template. + var show_whois = function(api_result) { + + // build the key/value hash for the record template + var recordData = { + 'Domain name': api_result.domainName, + 'Registered to': api_result.registrant && api_result.registrant.name, + 'Email': api_result.contactEmail, + 'Last updated': api_result.updatedDate && api_result.updatedDate.replace(/^(.*)?T(.*)?$/, '$1'), //trim time from the end + 'Expires': api_result.expiresDate && api_result.expiresDate.replace(/^(.*)?T(.*)?$/, '$1'), //trim time from the end }; - // trim times from end of date strings - api_result.updatedDate = api_result.updatedDate && api_result.updatedDate.replace(/^(.*)?T(.*)?$/, '$1'); - api_result.expirationDate = api_result.expirationDate && api_result.expirationDate.replace(/^(.*)?T(.*)?$/, '$1'); - Spice.add({ id: "domains", name: "Domains", - data: api_result, + data: { 'record_data': recordData }, meta: { sourceName: "WhoisAPI", sourceUrl: @@ -92,7 +111,7 @@ templates: { group: 'base', options:{ - content: template, + content: 'record', moreAt: true } } From f5161d9cc0ef40f77deb88328e396d6fa1984c6d Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 19 Jun 2014 13:03:50 +0000 Subject: [PATCH 09/45] Use Domainr for more info link. --- share/spice/domains/domains.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/share/spice/domains/domains.js b/share/spice/domains/domains.js index 29d2503832..3741ea6d42 100644 --- a/share/spice/domains/domains.js +++ b/share/spice/domains/domains.js @@ -68,11 +68,8 @@ name: "Domains", data: api_result, meta: { - sourceName: "WhoisAPI", - sourceUrl: - 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' - + api_result.domainName - + '&outputFormat=json' + sourceName: "Domainr", + sourceUrl: 'https://domai.nr/' + api_result.domainName }, templates: { group: 'base', @@ -101,12 +98,8 @@ name: "Domains", data: { 'record_data': recordData }, meta: { - sourceName: "WhoisAPI", - sourceUrl: - 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' - + api_result.domainName - + '&outputFormat=json' - + sourceName: "Domainr", + sourceUrl: 'https://domai.nr/' + api_result.domainName }, templates: { group: 'base', From 65ea4f2348ddf84c49c1a2fc49c95b80ca00a888 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 19 Jun 2014 13:06:23 +0000 Subject: [PATCH 10/45] Removed register language from the domain available template and removed custom whois template, now that we're using the record template. --- share/spice/domains/available.handlebars | 4 +--- share/spice/domains/whois.handlebars | 7 ------- 2 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 share/spice/domains/whois.handlebars diff --git a/share/spice/domains/available.handlebars b/share/spice/domains/available.handlebars index d99daeba85..f73a70c411 100644 --- a/share/spice/domains/available.handlebars +++ b/share/spice/domains/available.handlebars @@ -1,3 +1 @@ -
The domain {{domainName}} is probably available!
-
-
Register it at ______ [affiliate link goes here]
\ No newline at end of file +
The domain {{domainName}} is probably available!
\ No newline at end of file diff --git a/share/spice/domains/whois.handlebars b/share/spice/domains/whois.handlebars deleted file mode 100644 index 824f42bf5d..0000000000 --- a/share/spice/domains/whois.handlebars +++ /dev/null @@ -1,7 +0,0 @@ -
Whois for {{domainName}}
-
-
Registered to: {{registrantName}}
-
Email: {{contactEmail}}
-
Last updated: {{updatedDate}}
-
Expires: {{expirationDate}}
- From b49ec733fabc0bad6a877270c1dadacfae20f3ab Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 19 Jun 2014 15:00:49 +0000 Subject: [PATCH 11/45] Fixed triggering so all tests pass. Includes subdomain parsing in regex. --- lib/DDG/Spice/Domains.pm | 26 +++++++++++++------------- t/Domains.t | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Domains.pm index 75c1d91a0c..8b1e900acd 100644 --- a/lib/DDG/Spice/Domains.pm +++ b/lib/DDG/Spice/Domains.pm @@ -1,15 +1,16 @@ package DDG::Spice::Domains; - -# ABSTRACT: -# Returns an internet domain's availability and whois information. +# ABSTRACT: Returns an internet domain's availability and whois information. use DDG::Spice; +# flag for debugging output +my $is_debug = 0; + # regex for allowed TLDS (grabbed from main DDG code, in /lib/DDG/Util/Constants.pm) my $tlds_qr = qr/(?:c(?:o(?:m|op)?|at?|[iykgdmnxruhcfzvl])|o(?:rg|m)|n(?:et?|a(?:me)?|[ucgozrfpil])|e(?:d?u|[gechstr])|i(?:n(?:t|fo)?|[stqldroem])|m(?:o(?:bi)?|u(?:seum)?|i?l|[mcyvtsqhaerngxzfpwkd])|g(?:ov|[glqeriabtshdfmuywnp])|b(?:iz?|[drovfhtaywmzjsgbenl])|t(?:r(?:avel)?|[ncmfzdvkopthjwg]|e?l)|k[iemygznhwrp]|s[jtvberindlucygkhaozm]|u[gymszka]|h[nmutkr]|r[owesu]|d[kmzoej]|a(?:e(?:ro)?|r(?:pa)?|[qofiumsgzlwcnxdt])|p(?:ro?|[sgnthfymakwle])|v[aegiucn]|l[sayuvikcbrt]|j(?:o(?:bs)?|[mep])|w[fs]|z[amw]|f[rijkom]|y[eut]|qa)/i; # regex for parsing URLs -my $url_qr = qr/(?:http:\/\/)?(?:www\.)?([^\s]*?)\.($tlds_qr)(\:?[0-9]{1,4})?([^\s]*)/; +my $url_qr = qr/(?:http:\/\/)?([^\s\.]*\.)*([^\s\.]*?)\.($tlds_qr)(\:?[0-9]{1,4})?([^\s]*)/; # additional keywords that trigger this spice my $whois_keywords_qr = qr/whois|lookup|(?:is\s|)domain|(?:is\s|)available|register|owner(?:\sof|)|who\sowns|(?:how\sto\s|)buy/i; @@ -28,26 +29,25 @@ handle sub { return if !$query; # do not trigger this spice if the query is blank # parse the URL into its parts - my ($domain, $tld, $port, $resource_path) = $query =~ $url_qr; + my ($subdomains, $domain, $tld, $port, $resource_path) = $query =~ $url_qr; - warn $query, "\t", $domain || '', "\t", $tld || '', "\t", $port || '', "\t", $resource_path || ''; + warn 'query: ', $query, "\t", 'sub: ', $subdomains || '', "\t", 'domain: ', $domain || '', "\t", 'tld: ', $tld || '', "\t", 'port: ', $port || '', "\t", 'resource path: ', $resource_path || '' if $is_debug; # get the non-URL text from the query by combining the text before and after the match my $non_url_text = $` . $'; #' <-- close tick for syntax highlighting # is the string a naked domain, i.e. is there any text besides the domain - my $is_naked_domain = trim($non_url_text) ne ''; + my $is_naked_domain = trim($non_url_text) eq ''; # skip if no domain or tld return if !defined $domain || $domain eq '' || !defined $tld || $tld eq ''; - # skip if naked domain that contains a subdomain, port or resource_path - # (notice that we allow subdomains, ports, and resource paths when - # there is a whois keyword) + # skip if naked domain that contains a non-www subdomain, a port or a resource_path + # (notice that we allow subdomains, ports, and resource paths when there is a whois keyword) return if $is_naked_domain - && ($domain =~ /\./ - || (defined $port && $port ne '') - || (defined $resource_path && $resource_path ne '')); + && ( (defined $subdomains && $subdomains !~ /^www.$/) + || (defined $port && $port ne '') + || (defined $resource_path && $resource_path ne '')); # return the domain + the tld, with a period in between return lc "$domain.$tld"; diff --git a/t/Domains.t b/t/Domains.t index d9e9f41fec..c8a2f12a55 100644 --- a/t/Domains.t +++ b/t/Domains.t @@ -42,7 +42,7 @@ ddg_spice_test( 'duckduckgo.com/about' => undef, # Whois keywords with a resource path should trigger. - 'whoisduckduckgo.com/about' => expected_output_for('duckduckgo.com'), + 'whois duckduckgo.com/about' => expected_output_for('duckduckgo.com'), # A domain preceeded by 'http://' should trigger. 'http://duckduckgo.com' => expected_output_for('duckduckgo.com'), From 6f063b546a4eb5a4f15bf730dceeac20c72bc80c Mon Sep 17 00:00:00 2001 From: b1ake Date: Fri, 20 Jun 2014 16:24:09 +0000 Subject: [PATCH 12/45] Add links to Domainr, Namecheap and 101Domains when a domain is availalbe. --- share/spice/domains/available.handlebars | 4 +++- share/spice/domains/domains.js | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/share/spice/domains/available.handlebars b/share/spice/domains/available.handlebars index f73a70c411..12837139e5 100644 --- a/share/spice/domains/available.handlebars +++ b/share/spice/domains/available.handlebars @@ -1 +1,3 @@ -
The domain {{domainName}} is probably available!
\ No newline at end of file +
The domain {{domainName}} may be available!
+ + \ No newline at end of file diff --git a/share/spice/domains/domains.js b/share/spice/domains/domains.js index 3741ea6d42..0d96c6f8b8 100644 --- a/share/spice/domains/domains.js +++ b/share/spice/domains/domains.js @@ -74,8 +74,7 @@ templates: { group: 'base', options:{ - content: Spice.domains.available, - moreAt: true + content: Spice.domains.available } } }); From def4b847988094ce4ddc01e574e17c9e96f1260e Mon Sep 17 00:00:00 2001 From: b1ake Date: Fri, 20 Jun 2014 19:05:41 +0000 Subject: [PATCH 13/45] Updated to use WhoAPI. Their API output doesn't include the domain, so need to parse the URL from the query in JS or get the spice API to pass it thru. --- lib/DDG/Spice/Domains.pm | 4 +- share/spice/domains/available.handlebars | 2 +- share/spice/domains/domains.js | 101 +++++++++++++++++------ 3 files changed, 79 insertions(+), 28 deletions(-) diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Domains.pm index 8b1e900acd..874ec9814c 100644 --- a/lib/DDG/Spice/Domains.pm +++ b/lib/DDG/Spice/Domains.pm @@ -22,7 +22,9 @@ triggers query_raw => qr/^$url_qr$/, qr/^$whois_keywords_qr|$whois_keywords_qr$/; -spice to => 'https://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username={{ENV{DDG_SPICE_DOMAINS_USERNAME}}}&password={{ENV{DDG_SPICE_DOMAINS_PASSWORD}}}'; +# API call details for WhoAPI (https://whoapi.com/) +spice to => 'http://api.whoapi.com/?domain=$1&r=whois&apikey={{ENV{DDG_SPICE_DOMAINS_WHOAPI_KEY}}}'; +spice wrap_jsonp_callback => 1; handle sub { my ($query) = @_; diff --git a/share/spice/domains/available.handlebars b/share/spice/domains/available.handlebars index 12837139e5..e8caeeb533 100644 --- a/share/spice/domains/available.handlebars +++ b/share/spice/domains/available.handlebars @@ -1,3 +1,3 @@
The domain {{domainName}} may be available!
- \ No newline at end of file + \ No newline at end of file diff --git a/share/spice/domains/domains.js b/share/spice/domains/domains.js index 0d96c6f8b8..55cf2c380d 100644 --- a/share/spice/domains/domains.js +++ b/share/spice/domains/domains.js @@ -8,19 +8,30 @@ // for debugging if(is_debug) console.log('api_result:', api_result); - if (!api_result || api_result.error || !api_result.WhoisRecord) { + // Check for API error and exit early (with error message when in debug mode) + if (!api_result || api_result.status != 0) { + if(is_debug && api_result) { + console.log('Domains API failed with status code ' + api_result.status + (api_result.status_desc ? ' (' + api_result.status_desc + ')' : '') + '.'); + console.log(' -> for more info on status codes, see: https://whoapi.com/api-documentation.html#statuscodes'); + console.log(' -> raw api output:', api_result); + console.log(' '); + } + return Spice.failed('domains'); } - - // get the relevant part of the result - api_result = api_result.WhoisRecord; - + + // get the original query + var query = DDG.get_query(); + + // normalize the api output + api_result = normalize_api_output(api_result, query); + // is the domain available? var is_avail = is_domain_available(api_result); // if the domain isn't available, do we want to show // whois information? - var is_whois_allowed = is_whois_query(DDG.get_query()); + var is_whois_allowed = is_whois_query(query); // for debugging if(is_debug) console.log("is_avail:", is_avail, "is_whois_allowed:", is_whois_allowed); @@ -41,12 +52,7 @@ // Returns whether the domain is available, // based on the API result that was returned. var is_domain_available = function(api_result) { - - // no registrant means that a domain is available - // - // TODO: Figure out cases when domain is available but there - // is already a registrant, such as with expired domains. - return !api_result.registrant; + return !api_result['registered']; }; // Returns whether we should show whois data if this @@ -61,15 +67,64 @@ return /\s/.test(query.trim()); }; + var normalize_api_output = function(api_output, query) { + + // initialize the new output object + var normalized = { + // used but not displayed + 'domainName': '', + 'registered': false, + + // displayed, hence the capitalization and spaces + 'Registered to': '', + 'Email': '', + 'Last updated': '', + 'Expires': '' + }; + + // add domain to the API result, since WhoAPI doesn't add it automatically + // TODO: Need to parse the URL from this, b/c it's the fulll query + normalized['domainName'] = query; + normalized['registered'] = api_output['registered']; + + normalized['Registered to'] = get_first_by_key(api_output['contacts'], 'name'); + normalized['Email'] = get_first_by_key(api_output['contacts'], 'email'); + + // trim so dates ares shown without times + normalized['Last updated'] = api_output['date_updated'] && api_output['date_updated'].replace(/^(.*)?\s(.*)?$/, '$1'); + normalized['Expires'] = api_output['date_expires'] && api_output['date_expires'].replace(/^(.*)?\s(.*)?$/, '$1'); + + return normalized; + }; + + // Searches an array of objects for the first value + // at the specified key. + var get_first_by_key = function(arr, key) { + if(!arr || arr.length == 0) return null; + + // find the first object in the array that has a non-empty value at the key + var first = null; + arr.forEach( function(obj) { + if(obj && typeof obj[key] !== 'undefined' && obj[key] !== '') { + if(!first) first = obj[key]; + } + }); + + // return first, which could still be null + return first; + } + // Show message saying that the domain is available. var show_available = function(api_result) { + console.log('api result in show_available', api_result); + Spice.add({ id: "domains", name: "Domains", data: api_result, meta: { - sourceName: "Domainr", - sourceUrl: 'https://domai.nr/' + api_result.domainName + sourceName: "WhoAPI", + sourceUrl: 'https://whoapi.com/' // TODO: Ask if there's a GET url for user-friendly whois output (instead of ugly json output) }, templates: { group: 'base', @@ -83,22 +138,16 @@ // Show whois info for the domain using the 'record' template. var show_whois = function(api_result) { - // build the key/value hash for the record template - var recordData = { - 'Domain name': api_result.domainName, - 'Registered to': api_result.registrant && api_result.registrant.name, - 'Email': api_result.contactEmail, - 'Last updated': api_result.updatedDate && api_result.updatedDate.replace(/^(.*)?T(.*)?$/, '$1'), //trim time from the end - 'Expires': api_result.expiresDate && api_result.expiresDate.replace(/^(.*)?T(.*)?$/, '$1'), //trim time from the end - }; - Spice.add({ id: "domains", name: "Domains", - data: { 'record_data': recordData }, + data: { + 'record_data': api_result, + 'record_keys': ['Registered to', 'Email', 'Last updated', 'Expires'] + }, meta: { - sourceName: "Domainr", - sourceUrl: 'https://domai.nr/' + api_result.domainName + sourceName: "WhoAPI", + sourceUrl: 'https://whoapi.com/' // TODO: Ask if there's a GET url for user-friendly whois output (instead of ugly json output) }, templates: { group: 'base', From 47b4b78a9f5ddb95fde1390b5bcb4b5068935846 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 26 Jun 2014 19:39:46 +0000 Subject: [PATCH 14/45] Switched api back to whoisxmlapi.com (ddg already has an api key for them). Updated API handling and output templates. --- lib/DDG/Spice/Domains.pm | 5 +- share/spice/domains/available.handlebars | 6 +- share/spice/domains/domains.js | 77 +++++++++++++----------- 3 files changed, 47 insertions(+), 41 deletions(-) diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Domains.pm index 874ec9814c..f1d3f7b63e 100644 --- a/lib/DDG/Spice/Domains.pm +++ b/lib/DDG/Spice/Domains.pm @@ -23,10 +23,9 @@ triggers query_raw => qr/^$whois_keywords_qr|$whois_keywords_qr$/; # API call details for WhoAPI (https://whoapi.com/) -spice to => 'http://api.whoapi.com/?domain=$1&r=whois&apikey={{ENV{DDG_SPICE_DOMAINS_WHOAPI_KEY}}}'; -spice wrap_jsonp_callback => 1; +spice to => 'https://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username={{ENV{DDG_SPICE_DOMAINS_USERNAME}}}&password={{ENV{DDG_SPICE_DOMAINS_PASSWORD}}}'; -handle sub { +handle sub { my ($query) = @_; return if !$query; # do not trigger this spice if the query is blank diff --git a/share/spice/domains/available.handlebars b/share/spice/domains/available.handlebars index e8caeeb533..31225b9c8a 100644 --- a/share/spice/domains/available.handlebars +++ b/share/spice/domains/available.handlebars @@ -1,3 +1,3 @@ -
The domain {{domainName}} may be available!
- - \ No newline at end of file +The domain {{domainName}} may be available!
+To register, try Domainr, NameCheap or 101Domains
+
\ No newline at end of file diff --git a/share/spice/domains/domains.js b/share/spice/domains/domains.js index 55cf2c380d..4422c1bd52 100644 --- a/share/spice/domains/domains.js +++ b/share/spice/domains/domains.js @@ -9,29 +9,21 @@ if(is_debug) console.log('api_result:', api_result); // Check for API error and exit early (with error message when in debug mode) - if (!api_result || api_result.status != 0) { - if(is_debug && api_result) { - console.log('Domains API failed with status code ' + api_result.status + (api_result.status_desc ? ' (' + api_result.status_desc + ')' : '') + '.'); - console.log(' -> for more info on status codes, see: https://whoapi.com/api-documentation.html#statuscodes'); - console.log(' -> raw api output:', api_result); - console.log(' '); - } - - return Spice.failed('domains'); - } + if (!api_result || api_result.error || !api_result.WhoisRecord) { + if(is_debug) console.log("Error with whois API. api_result:", api_result || 'undefined'); - // get the original query - var query = DDG.get_query(); + return Spice.failed('domains'); + } // normalize the api output - api_result = normalize_api_output(api_result, query); + api_result = normalize_api_output(api_result); // is the domain available? var is_avail = is_domain_available(api_result); // if the domain isn't available, do we want to show // whois information? - var is_whois_allowed = is_whois_query(query); + var is_whois_allowed = is_whois_query(DDG.get_query()); // for debugging if(is_debug) console.log("is_avail:", is_avail, "is_whois_allowed:", is_whois_allowed); @@ -67,32 +59,42 @@ return /\s/.test(query.trim()); }; - var normalize_api_output = function(api_output, query) { + var normalize_api_output = function(api_output) { - // initialize the new output object + // initialize the output object var normalized = { - // used but not displayed + // these fields will not be displayed, but are used internally 'domainName': '', 'registered': false, - // displayed, hence the capitalization and spaces + // these fields will displayed, hence the capitalization and spaces 'Registered to': '', 'Email': '', 'Last updated': '', 'Expires': '' }; - - // add domain to the API result, since WhoAPI doesn't add it automatically - // TODO: Need to parse the URL from this, b/c it's the fulll query - normalized['domainName'] = query; - normalized['registered'] = api_output['registered']; - - normalized['Registered to'] = get_first_by_key(api_output['contacts'], 'name'); - normalized['Email'] = get_first_by_key(api_output['contacts'], 'email'); - - // trim so dates ares shown without times - normalized['Last updated'] = api_output['date_updated'] && api_output['date_updated'].replace(/^(.*)?\s(.*)?$/, '$1'); - normalized['Expires'] = api_output['date_expires'] && api_output['date_expires'].replace(/^(.*)?\s(.*)?$/, '$1'); + + // get the domain name + normalized['domainName'] = api_output.WhoisRecord.domainName; + + // set the registered flag + normalized['registered'] = !!api_output.WhoisRecord.registrant; + + // get contact name and email from the registrant, + // and falling back to the admin and technical contacts + var contacts = [ + api_output.WhoisRecord.registrant, + api_output.WhoisRecord.administrativeContact, + api_output.WhoisRecord.technicalContact + ]; + normalized['Registered to'] = get_first_by_key(contacts, 'name'); + normalized['Email'] = get_first_by_key(contacts, 'email'); + + // trim dates so they are shown without times + normalized['Last updated'] = api_output.WhoisRecord.updatedDate + && api_output.WhoisRecord.updatedDate.replace(/^(.*)?\s(.*)?$/, '$1'); + normalized['Expires'] = api_output.WhoisRecord.expiresDate + && api_output.WhoisRecord.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'); return normalized; }; @@ -123,13 +125,16 @@ name: "Domains", data: api_result, meta: { - sourceName: "WhoAPI", - sourceUrl: 'https://whoapi.com/' // TODO: Ask if there's a GET url for user-friendly whois output (instead of ugly json output) + sourceName: "Whois API", + sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' + + api_result.domainName + + '&outputFormat=json' }, templates: { group: 'base', options:{ - content: Spice.domains.available + content: Spice.domains.available, + moreAt: true } } }); @@ -146,8 +151,10 @@ 'record_keys': ['Registered to', 'Email', 'Last updated', 'Expires'] }, meta: { - sourceName: "WhoAPI", - sourceUrl: 'https://whoapi.com/' // TODO: Ask if there's a GET url for user-friendly whois output (instead of ugly json output) + sourceName: "Whois API", + sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' + + api_result.domainName + + '&outputFormat=json' }, templates: { group: 'base', From 7a350f4b56ad8699086b8e3734310ae0ebf6e3dd Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 26 Jun 2014 19:52:23 +0000 Subject: [PATCH 15/45] Improved comments. --- lib/DDG/Spice/Domains.pm | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Domains.pm index f1d3f7b63e..7070a796a0 100644 --- a/lib/DDG/Spice/Domains.pm +++ b/lib/DDG/Spice/Domains.pm @@ -3,10 +3,10 @@ package DDG::Spice::Domains; use DDG::Spice; -# flag for debugging output +# turns on/off debugging output my $is_debug = 0; -# regex for allowed TLDS (grabbed from main DDG code, in /lib/DDG/Util/Constants.pm) +# regex for allowed TLDS (grabbed from DDG core repo, in /lib/DDG/Util/Constants.pm) my $tlds_qr = qr/(?:c(?:o(?:m|op)?|at?|[iykgdmnxruhcfzvl])|o(?:rg|m)|n(?:et?|a(?:me)?|[ucgozrfpil])|e(?:d?u|[gechstr])|i(?:n(?:t|fo)?|[stqldroem])|m(?:o(?:bi)?|u(?:seum)?|i?l|[mcyvtsqhaerngxzfpwkd])|g(?:ov|[glqeriabtshdfmuywnp])|b(?:iz?|[drovfhtaywmzjsgbenl])|t(?:r(?:avel)?|[ncmfzdvkopthjwg]|e?l)|k[iemygznhwrp]|s[jtvberindlucygkhaozm]|u[gymszka]|h[nmutkr]|r[owesu]|d[kmzoej]|a(?:e(?:ro)?|r(?:pa)?|[qofiumsgzlwcnxdt])|p(?:ro?|[sgnthfymakwle])|v[aegiucn]|l[sayuvikcbrt]|j(?:o(?:bs)?|[mep])|w[fs]|z[amw]|f[rijkom]|y[eut]|qa)/i; # regex for parsing URLs @@ -15,14 +15,18 @@ my $url_qr = qr/(?:http:\/\/)?([^\s\.]*\.)*([^\s\.]*?)\.($tlds_qr)(\:?[0-9]{1,4} # additional keywords that trigger this spice my $whois_keywords_qr = qr/whois|lookup|(?:is\s|)domain|(?:is\s|)available|register|owner(?:\sof|)|who\sowns|(?:how\sto\s|)buy/i; -# trigger this spice when: +# trigger this spice when either: # - query contains only a URL -# - query starts or end with any of the whois keywords +# - query contains starts or end with any of the whois keywords +# +# note that there are additional guards in the handle() function that +# narrow this spice's query space. +# triggers query_raw => qr/^$url_qr$/, qr/^$whois_keywords_qr|$whois_keywords_qr$/; -# API call details for WhoAPI (https://whoapi.com/) +# API call details for Whois API (http://www.whoisxmlapi.com/) spice to => 'https://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username={{ENV{DDG_SPICE_DOMAINS_USERNAME}}}&password={{ENV{DDG_SPICE_DOMAINS_PASSWORD}}}'; handle sub { @@ -32,25 +36,36 @@ handle sub { # parse the URL into its parts my ($subdomains, $domain, $tld, $port, $resource_path) = $query =~ $url_qr; + # debugging output warn 'query: ', $query, "\t", 'sub: ', $subdomains || '', "\t", 'domain: ', $domain || '', "\t", 'tld: ', $tld || '', "\t", 'port: ', $port || '', "\t", 'resource path: ', $resource_path || '' if $is_debug; # get the non-URL text from the query by combining the text before and after the match - my $non_url_text = $` . $'; #' <-- close tick for syntax highlighting + my $non_url_text = $` . $'; #' <-- closing tick added for syntax highlighting - # is the string a naked domain, i.e. is there any text besides the domain + # is the string a naked domain, i.e. is there any text besides the domain? my $is_naked_domain = trim($non_url_text) eq ''; - # skip if no domain or tld + # skip if we're missing a domain or a tld return if !defined $domain || $domain eq '' || !defined $tld || $tld eq ''; - # skip if naked domain that contains a non-www subdomain, a port or a resource_path - # (notice that we allow subdomains, ports, and resource paths when there is a whois keyword) + # skip if we have naked domain that contains a non-www subdomain, a port or a resource_path. + # e.g. continue: 'http://duckduckgo.com' is allowed + # skip: 'http://blog.duckduckgo.com' + # skip: 'http://duckduckgo.com:8080' + # skip: 'http://blog.duckduckgo.com/hello.html' + # + # note that if the user includes a whois keyword to any of these, + # such as 'whois http://blog.duckduckgo.com', they we continue. + # + # this signals to us that the user wants a whois result, and isn't just + # trying to nav to the URL they typed. + # return if $is_naked_domain && ( (defined $subdomains && $subdomains !~ /^www.$/) || (defined $port && $port ne '') || (defined $resource_path && $resource_path ne '')); - # return the domain + the tld, with a period in between + # return the combined domain + tld (after adding a period in between) return lc "$domain.$tld"; }; From dfcdac76da72e16fab91d71a35b48cf606b14297 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 26 Jun 2014 19:54:13 +0000 Subject: [PATCH 16/45] Renamed varaible api_result to api_output. --- share/spice/domains/domains.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/share/spice/domains/domains.js b/share/spice/domains/domains.js index 4422c1bd52..58752633f5 100644 --- a/share/spice/domains/domains.js +++ b/share/spice/domains/domains.js @@ -4,22 +4,22 @@ // flag for debugging output var is_debug = true; - env.ddg_spice_domains = function(api_result) { + env.ddg_spice_domains = function(api_output) { // for debugging - if(is_debug) console.log('api_result:', api_result); + if(is_debug) console.log('api_output:', api_output); // Check for API error and exit early (with error message when in debug mode) - if (!api_result || api_result.error || !api_result.WhoisRecord) { - if(is_debug) console.log("Error with whois API. api_result:", api_result || 'undefined'); + if (!api_output || api_output.error || !api_output.WhoisRecord) { + if(is_debug) console.log("Error with whois API. api_output:", api_output || 'undefined'); return Spice.failed('domains'); } // normalize the api output - api_result = normalize_api_output(api_result); + api_output = normalize_api_output(api_output); // is the domain available? - var is_avail = is_domain_available(api_result); + var is_avail = is_domain_available(api_output); // if the domain isn't available, do we want to show // whois information? @@ -31,10 +31,10 @@ // decide which template to show, if any if(is_avail) { // show message saying the domain is available - show_available(api_result); + show_available(api_output); } else if(is_whois_allowed) { // show whois info for the domain - show_whois(api_result); + show_whois(api_output); } else { // by default, show nothing } @@ -43,8 +43,8 @@ // Returns whether the domain is available, // based on the API result that was returned. - var is_domain_available = function(api_result) { - return !api_result['registered']; + var is_domain_available = function(api_output) { + return !api_output['registered']; }; // Returns whether we should show whois data if this @@ -117,17 +117,17 @@ } // Show message saying that the domain is available. - var show_available = function(api_result) { - console.log('api result in show_available', api_result); + var show_available = function(api_output) { + console.log('api result in show_available', api_output); Spice.add({ id: "domains", name: "Domains", - data: api_result, + data: api_output, meta: { sourceName: "Whois API", sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' - + api_result.domainName + + api_output.domainName + '&outputFormat=json' }, templates: { @@ -141,19 +141,19 @@ }; // Show whois info for the domain using the 'record' template. - var show_whois = function(api_result) { + var show_whois = function(api_output) { Spice.add({ id: "domains", name: "Domains", data: { - 'record_data': api_result, + 'record_data': api_output, 'record_keys': ['Registered to', 'Email', 'Last updated', 'Expires'] }, meta: { sourceName: "Whois API", sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' - + api_result.domainName + + api_output.domainName + '&outputFormat=json' }, templates: { From bbbe168bf984b4fabf181750faac2601e4ddd837 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 26 Jun 2014 19:57:45 +0000 Subject: [PATCH 17/45] More comments. --- share/spice/domains/domains.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/share/spice/domains/domains.js b/share/spice/domains/domains.js index 58752633f5..a6ff158cb4 100644 --- a/share/spice/domains/domains.js +++ b/share/spice/domains/domains.js @@ -4,11 +4,13 @@ // flag for debugging output var is_debug = true; + // spice callback function env.ddg_spice_domains = function(api_output) { // for debugging if(is_debug) console.log('api_output:', api_output); - // Check for API error and exit early (with error message when in debug mode) + // Check for API error and exit early if found + // (with error message when in debug mode) if (!api_output || api_output.error || !api_output.WhoisRecord) { if(is_debug) console.log("Error with whois API. api_output:", api_output || 'undefined'); @@ -55,7 +57,7 @@ if(is_debug) console.log('in is_whois_query, query =', query); // show whois results except when the query contains only the domain - // and no other keywords, which we test by looking for spaces in the query. + // and no other keywords, which we test by looking for a space in the query. return /\s/.test(query.trim()); }; @@ -63,11 +65,11 @@ // initialize the output object var normalized = { - // these fields will not be displayed, but are used internally + // these fields are not displayed, but are used internally 'domainName': '', 'registered': false, - // these fields will displayed, hence the capitalization and spaces + // these fields are displayed (hence the user-friendly capitalization and spaces) 'Registered to': '', 'Email': '', 'Last updated': '', @@ -77,7 +79,7 @@ // get the domain name normalized['domainName'] = api_output.WhoisRecord.domainName; - // set the registered flag + // store whether the domain is registered normalized['registered'] = !!api_output.WhoisRecord.registrant; // get contact name and email from the registrant, From 5b452aaa6b2b2c6be5a97a93e5ce50eadcc9f571 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 26 Jun 2014 19:59:35 +0000 Subject: [PATCH 18/45] Turn off debugging output. --- share/spice/domains/domains.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/spice/domains/domains.js b/share/spice/domains/domains.js index a6ff158cb4..671a1de8fd 100644 --- a/share/spice/domains/domains.js +++ b/share/spice/domains/domains.js @@ -1,8 +1,8 @@ (function (env) { "use strict"; - // flag for debugging output - var is_debug = true; + // turns on/off debugging output + var is_debug = false; // spice callback function env.ddg_spice_domains = function(api_output) { From cdfb4602edaacdb1cec8b4a9c7ae836467575d2b Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 26 Jun 2014 20:08:00 +0000 Subject: [PATCH 19/45] Rename spice from 'Domains' -> 'Whois'. --- lib/DDG/Spice/{Domains.pm => Whois.pm} | 0 share/spice/{domains => whois}/available.handlebars | 0 share/spice/{domains/domains.js => whois/whois.js} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename lib/DDG/Spice/{Domains.pm => Whois.pm} (100%) rename share/spice/{domains => whois}/available.handlebars (100%) rename share/spice/{domains/domains.js => whois/whois.js} (100%) diff --git a/lib/DDG/Spice/Domains.pm b/lib/DDG/Spice/Whois.pm similarity index 100% rename from lib/DDG/Spice/Domains.pm rename to lib/DDG/Spice/Whois.pm diff --git a/share/spice/domains/available.handlebars b/share/spice/whois/available.handlebars similarity index 100% rename from share/spice/domains/available.handlebars rename to share/spice/whois/available.handlebars diff --git a/share/spice/domains/domains.js b/share/spice/whois/whois.js similarity index 100% rename from share/spice/domains/domains.js rename to share/spice/whois/whois.js From 7a769583fc72b970799dfcaa48957f17c946bb6e Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 26 Jun 2014 20:12:12 +0000 Subject: [PATCH 20/45] Renamed test file. --- t/{Domains.t => Whois.t} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename t/{Domains.t => Whois.t} (97%) diff --git a/t/Domains.t b/t/Whois.t similarity index 97% rename from t/Domains.t rename to t/Whois.t index c8a2f12a55..99ff98a96f 100644 --- a/t/Domains.t +++ b/t/Whois.t @@ -9,7 +9,7 @@ use DDG::Test::Spice; ddg_spice_test( # This is the name of the Spice that will be loaded to test. - [ 'DDG::Spice::Domains' ], + [ 'DDG::Spice::Whois' ], # A naked domain should trigger. 'duckduckgo.com' => expected_output_for('duckduckgo.com'), @@ -115,9 +115,9 @@ sub expected_output_for { # return the output we expect for the spice test return test_spice( - '/js/spice/domains/' . $domain_expected, + '/js/spice/whois/' . $domain_expected, call_type => 'include', - caller => 'DDG::Spice::Domains', + caller => 'DDG::Spice::Whois', ); } From 25850f0f650dd6cdcd34ecbee455d92a1c2c18d9 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 26 Jun 2014 20:18:00 +0000 Subject: [PATCH 21/45] Fixes related to renaming the spice. --- lib/DDG/Spice/Whois.pm | 4 ++-- share/spice/whois/whois.js | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/DDG/Spice/Whois.pm b/lib/DDG/Spice/Whois.pm index 7070a796a0..3bd2fd1331 100644 --- a/lib/DDG/Spice/Whois.pm +++ b/lib/DDG/Spice/Whois.pm @@ -1,4 +1,4 @@ -package DDG::Spice::Domains; +package DDG::Spice::Whois; # ABSTRACT: Returns an internet domain's availability and whois information. use DDG::Spice; @@ -27,7 +27,7 @@ triggers query_raw => qr/^$whois_keywords_qr|$whois_keywords_qr$/; # API call details for Whois API (http://www.whoisxmlapi.com/) -spice to => 'https://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username={{ENV{DDG_SPICE_DOMAINS_USERNAME}}}&password={{ENV{DDG_SPICE_DOMAINS_PASSWORD}}}'; +spice to => 'https://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username={{ENV{DDG_SPICE_WHOIS_USERNAME}}}&password={{ENV{DDG_SPICE_WHOIS_PASSWORD}}}'; handle sub { my ($query) = @_; diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 671a1de8fd..6ac7025ccf 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -5,7 +5,7 @@ var is_debug = false; // spice callback function - env.ddg_spice_domains = function(api_output) { + env.ddg_spice_whois = function(api_output) { // for debugging if(is_debug) console.log('api_output:', api_output); @@ -14,7 +14,7 @@ if (!api_output || api_output.error || !api_output.WhoisRecord) { if(is_debug) console.log("Error with whois API. api_output:", api_output || 'undefined'); - return Spice.failed('domains'); + return Spice.failed('whois'); } // normalize the api output @@ -123,8 +123,8 @@ console.log('api result in show_available', api_output); Spice.add({ - id: "domains", - name: "Domains", + id: "whois", + name: "Whois", data: api_output, meta: { sourceName: "Whois API", @@ -135,7 +135,7 @@ templates: { group: 'base', options:{ - content: Spice.domains.available, + content: Spice.whois.available, moreAt: true } } @@ -146,8 +146,8 @@ var show_whois = function(api_output) { Spice.add({ - id: "domains", - name: "Domains", + id: "whois", + name: "Whois", data: { 'record_data': api_output, 'record_keys': ['Registered to', 'Email', 'Last updated', 'Expires'] From 1514b7e6c73ed8301d5f2a17368d624387428790 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 26 Jun 2014 20:29:20 +0000 Subject: [PATCH 22/45] Added metadata. --- lib/DDG/Spice/Whois.pm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/DDG/Spice/Whois.pm b/lib/DDG/Spice/Whois.pm index 3bd2fd1331..05774e6d64 100644 --- a/lib/DDG/Spice/Whois.pm +++ b/lib/DDG/Spice/Whois.pm @@ -3,6 +3,20 @@ package DDG::Spice::Whois; use DDG::Spice; +# Metadata for this spice +name 'Whois'; +source 'Whois API'; +description 'Whois info and registration links for web domains'; +primary_example_queries 'whois duckduckgo.com', 'whois http://duckduckgo.com'; +secondary_example_queries 'domain duckduckgo.com', 'who owns duckduckgo.com', 'duckduckgo.com available'; +category 'programming'; +topics 'computing', 'geek', 'programming', 'sysadmin'; +code_url 'https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/Whois.pm'; + +attribution twitter => 'bjennelle', + github => ["b1ake", 'Blake Jennelle']; + + # turns on/off debugging output my $is_debug = 0; From a971027f0601510979febdc5d4940417d9759caa Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 10 Jul 2014 18:44:04 +0000 Subject: [PATCH 23/45] Use jQuery's each() function instead of Array.forEach(). Also simplify an if statement. --- share/spice/whois/whois.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 6ac7025ccf..42adb67b18 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -108,9 +108,15 @@ // find the first object in the array that has a non-empty value at the key var first = null; - arr.forEach( function(obj) { - if(obj && typeof obj[key] !== 'undefined' && obj[key] !== '') { - if(!first) first = obj[key]; + $.each(arr, function(index, obj) { + // get the value at the specified key + // (which could be undefined) + var value = obj && obj[key]; + + // update the first var if the value is truthy + // and first hasn't already been found + if(!first && value) { + first = value; } }); @@ -120,7 +126,7 @@ // Show message saying that the domain is available. var show_available = function(api_output) { - console.log('api result in show_available', api_output); + if(is_debug) console.log('api result in show_available', api_output); Spice.add({ id: "whois", From ce3c4d2ebe162075c55191a99f4251c012c1798b Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 10 Jul 2014 19:07:34 +0000 Subject: [PATCH 24/45] Simplify the api output normalizing step. --- share/spice/whois/whois.js | 57 ++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 42adb67b18..c1a89569cb 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -63,43 +63,40 @@ var normalize_api_output = function(api_output) { - // initialize the output object - var normalized = { - // these fields are not displayed, but are used internally - 'domainName': '', - 'registered': false, - - // these fields are displayed (hence the user-friendly capitalization and spaces) - 'Registered to': '', - 'Email': '', - 'Last updated': '', - 'Expires': '' - }; - - // get the domain name - normalized['domainName'] = api_output.WhoisRecord.domainName; - - // store whether the domain is registered - normalized['registered'] = !!api_output.WhoisRecord.registrant; - - // get contact name and email from the registrant, - // and falling back to the admin and technical contacts + // store the domain's various contacts in an array. + // + // we'll iterate through this array in order, using + // info from the first contact that contains the field we want. var contacts = [ api_output.WhoisRecord.registrant, api_output.WhoisRecord.administrativeContact, api_output.WhoisRecord.technicalContact ]; - normalized['Registered to'] = get_first_by_key(contacts, 'name'); - normalized['Email'] = get_first_by_key(contacts, 'email'); - // trim dates so they are shown without times - normalized['Last updated'] = api_output.WhoisRecord.updatedDate - && api_output.WhoisRecord.updatedDate.replace(/^(.*)?\s(.*)?$/, '$1'); - normalized['Expires'] = api_output.WhoisRecord.expiresDate - && api_output.WhoisRecord.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'); + // return the normalized output as a hash + return { - return normalized; - }; + // these first fields are not displayed + // (hence the camelCase, which the user will not see) + + 'domainName': api_output.WhoisRecord.domainName, + 'registered': !!api_output.WhoisRecord.registrant, // boolean flag + + // the remaining fields are displayed + // (hence the user-friendly capitalization and spaces) + + 'Registered to': get_first_by_key(contacts, 'name'), + 'Email': get_first_by_key(contacts, 'email'), + + // trim dates so they are shown without times + 'Last updated': api_output.WhoisRecord.updatedDate + && api_output.WhoisRecord.updatedDate.replace(/^(.*)?\s(.*)?$/, '$1'), + + 'Expires': api_output.WhoisRecord.expiresDate + && api_output.WhoisRecord.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'), + + }; + } // Searches an array of objects for the first value // at the specified key. From c4379752ec4f09c172f9a44fca2124a4f0a9c6ae Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 10 Jul 2014 19:22:56 +0000 Subject: [PATCH 25/45] Share data between the two different template calls for DRYer code. --- share/spice/whois/whois.js | 60 ++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index c1a89569cb..3c6ddbf03d 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -121,54 +121,52 @@ return first; } - // Show message saying that the domain is available. - var show_available = function(api_output) { - if(is_debug) console.log('api result in show_available', api_output); - - Spice.add({ + // Data that's shared between the two Spice.add calls. + var get_shared_spice_data = function(api_output) { + return { id: "whois", name: "Whois", - data: api_output, meta: { - sourceName: "Whois API", - sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' + sourceName: "Whois API", + sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' + api_output.domainName + '&outputFormat=json' }, templates: { group: 'base', options:{ - content: Spice.whois.available, moreAt: true } } - }); + }; + }; + + // Show message saying that the domain is available. + var show_available = function(api_output) { + if(is_debug) console.log('api result in show_available', api_output); + + var shared_spice_data = get_shared_spice_data(api_output); + + // add the attributes specific to this template + shared_spice_data.data = api_output; + shared_spice_data.templates.options.content = Spice.whois.available; + + Spice.add(shared_spice_data); }; // Show whois info for the domain using the 'record' template. var show_whois = function(api_output) { - Spice.add({ - id: "whois", - name: "Whois", - data: { - 'record_data': api_output, - 'record_keys': ['Registered to', 'Email', 'Last updated', 'Expires'] - }, - meta: { - sourceName: "Whois API", - sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' - + api_output.domainName - + '&outputFormat=json' - }, - templates: { - group: 'base', - options:{ - content: 'record', - moreAt: true - } - } - }); + var shared_spice_data = get_shared_spice_data(api_output); + + // add the attributes specific to this template + shared_spice_data.data = { + 'record_data': api_output, + 'record_keys': ['Registered to', 'Email', 'Last updated', 'Expires'] + }; + shared_spice_data.templates.options.content = 'record'; + + Spice.add(shared_spice_data); }; From 0784c5bd13f0aba14f81f2f5373899f8fb775a3c Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 10 Jul 2014 19:25:28 +0000 Subject: [PATCH 26/45] Use http instead of https endpoint for speed. --- lib/DDG/Spice/Whois.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/DDG/Spice/Whois.pm b/lib/DDG/Spice/Whois.pm index 05774e6d64..ba1bccf701 100644 --- a/lib/DDG/Spice/Whois.pm +++ b/lib/DDG/Spice/Whois.pm @@ -41,7 +41,7 @@ triggers query_raw => qr/^$whois_keywords_qr|$whois_keywords_qr$/; # API call details for Whois API (http://www.whoisxmlapi.com/) -spice to => 'https://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username={{ENV{DDG_SPICE_WHOIS_USERNAME}}}&password={{ENV{DDG_SPICE_WHOIS_PASSWORD}}}'; +spice to => 'http://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username={{ENV{DDG_SPICE_WHOIS_USERNAME}}}&password={{ENV{DDG_SPICE_WHOIS_PASSWORD}}}'; handle sub { my ($query) = @_; From d56e8a2db74a80240b6f2bce4f6e25fefd8340b4 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 10 Jul 2014 20:51:22 +0000 Subject: [PATCH 27/45] Added comments to URL parsing regex. --- lib/DDG/Spice/Whois.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/DDG/Spice/Whois.pm b/lib/DDG/Spice/Whois.pm index ba1bccf701..83d7388e85 100644 --- a/lib/DDG/Spice/Whois.pm +++ b/lib/DDG/Spice/Whois.pm @@ -24,7 +24,12 @@ my $is_debug = 0; my $tlds_qr = qr/(?:c(?:o(?:m|op)?|at?|[iykgdmnxruhcfzvl])|o(?:rg|m)|n(?:et?|a(?:me)?|[ucgozrfpil])|e(?:d?u|[gechstr])|i(?:n(?:t|fo)?|[stqldroem])|m(?:o(?:bi)?|u(?:seum)?|i?l|[mcyvtsqhaerngxzfpwkd])|g(?:ov|[glqeriabtshdfmuywnp])|b(?:iz?|[drovfhtaywmzjsgbenl])|t(?:r(?:avel)?|[ncmfzdvkopthjwg]|e?l)|k[iemygznhwrp]|s[jtvberindlucygkhaozm]|u[gymszka]|h[nmutkr]|r[owesu]|d[kmzoej]|a(?:e(?:ro)?|r(?:pa)?|[qofiumsgzlwcnxdt])|p(?:ro?|[sgnthfymakwle])|v[aegiucn]|l[sayuvikcbrt]|j(?:o(?:bs)?|[mep])|w[fs]|z[amw]|f[rijkom]|y[eut]|qa)/i; # regex for parsing URLs -my $url_qr = qr/(?:http:\/\/)?([^\s\.]*\.)*([^\s\.]*?)\.($tlds_qr)(\:?[0-9]{1,4})?([^\s]*)/; +my $url_qr = qr/(?:http:\/\/)? # require http + ([^\s\.]*\.)* # capture any subdomains + ([^\s\.]*?) # capture the domain + \.($tlds_qr) # capture the tld + (\:?[0-9]{1,4})? # look for a port, such as L3000 + ([^\s]*)/x; # look for an extended path # additional keywords that trigger this spice my $whois_keywords_qr = qr/whois|lookup|(?:is\s|)domain|(?:is\s|)available|register|owner(?:\sof|)|who\sowns|(?:how\sto\s|)buy/i; From c6f4ec2ac7deff16a293e7f456edc6f5e07c4e01 Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 10 Jul 2014 21:03:00 +0000 Subject: [PATCH 28/45] Tweak a few comments. --- lib/DDG/Spice/Whois.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/DDG/Spice/Whois.pm b/lib/DDG/Spice/Whois.pm index 83d7388e85..e1cbeeda57 100644 --- a/lib/DDG/Spice/Whois.pm +++ b/lib/DDG/Spice/Whois.pm @@ -27,9 +27,9 @@ my $tlds_qr = qr/(?:c(?:o(?:m|op)?|at?|[iykgdmnxruhcfzvl])|o(?:rg|m)|n(?:et?|a(? my $url_qr = qr/(?:http:\/\/)? # require http ([^\s\.]*\.)* # capture any subdomains ([^\s\.]*?) # capture the domain - \.($tlds_qr) # capture the tld - (\:?[0-9]{1,4})? # look for a port, such as L3000 - ([^\s]*)/x; # look for an extended path + \.($tlds_qr) # capture the tld, such as .com + (\:?[0-9]{1,4})? # look for a port, such as :3000 + ([^\s]*)/x; # look for an extended path, such as /pages/about.htm # additional keywords that trigger this spice my $whois_keywords_qr = qr/whois|lookup|(?:is\s|)domain|(?:is\s|)available|register|owner(?:\sof|)|who\sowns|(?:how\sto\s|)buy/i; From 3956786e2c18b4da262b2d7b16a0fc5df30535ed Mon Sep 17 00:00:00 2001 From: b1ake Date: Thu, 10 Jul 2014 21:10:41 +0000 Subject: [PATCH 29/45] Changed link to 'More at Whois API' so that it shows a raw text whois, instead of a json whois. --- share/spice/whois/whois.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 3c6ddbf03d..1a7fe767fd 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -130,7 +130,7 @@ sourceName: "Whois API", sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' + api_output.domainName - + '&outputFormat=json' + + '&outputFormat=json&target=raw' }, templates: { group: 'base', From 1c42c09cc9d3e4f220cc097e02712ac279d7f35c Mon Sep 17 00:00:00 2001 From: b1ake Date: Fri, 11 Jul 2014 18:58:41 +0000 Subject: [PATCH 30/45] Use jquery trim() instead of standard JS trim() from IE 8 compatibility. --- share/spice/whois/whois.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 1a7fe767fd..3b1e54f30e 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -58,7 +58,7 @@ // show whois results except when the query contains only the domain // and no other keywords, which we test by looking for a space in the query. - return /\s/.test(query.trim()); + return /\s/.test($.trim(query)); }; var normalize_api_output = function(api_output) { From 1343798a959dff90328d94bbb8dbbb5656dc00d7 Mon Sep 17 00:00:00 2001 From: b1ake Date: Fri, 11 Jul 2014 19:00:51 +0000 Subject: [PATCH 31/45] Rename api_output -> api_result, which is DDG-preferred variable name. --- share/spice/whois/whois.js | 58 +++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 3b1e54f30e..73e46b121b 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -5,23 +5,23 @@ var is_debug = false; // spice callback function - env.ddg_spice_whois = function(api_output) { + env.ddg_spice_whois = function(api_result) { // for debugging - if(is_debug) console.log('api_output:', api_output); + if(is_debug) console.log('api_result:', api_result); // Check for API error and exit early if found // (with error message when in debug mode) - if (!api_output || api_output.error || !api_output.WhoisRecord) { - if(is_debug) console.log("Error with whois API. api_output:", api_output || 'undefined'); + if (!api_result || api_result.error || !api_result.WhoisRecord) { + if(is_debug) console.log("Error with whois API. api_result:", api_result || 'undefined'); return Spice.failed('whois'); } // normalize the api output - api_output = normalize_api_output(api_output); + api_result = normalize_api_result(api_result); // is the domain available? - var is_avail = is_domain_available(api_output); + var is_avail = is_domain_available(api_result); // if the domain isn't available, do we want to show // whois information? @@ -33,10 +33,10 @@ // decide which template to show, if any if(is_avail) { // show message saying the domain is available - show_available(api_output); + show_available(api_result); } else if(is_whois_allowed) { // show whois info for the domain - show_whois(api_output); + show_whois(api_result); } else { // by default, show nothing } @@ -45,8 +45,8 @@ // Returns whether the domain is available, // based on the API result that was returned. - var is_domain_available = function(api_output) { - return !api_output['registered']; + var is_domain_available = function(api_result) { + return !api_result['registered']; }; // Returns whether we should show whois data if this @@ -61,16 +61,16 @@ return /\s/.test($.trim(query)); }; - var normalize_api_output = function(api_output) { + var normalize_api_result = function(api_result) { // store the domain's various contacts in an array. // // we'll iterate through this array in order, using // info from the first contact that contains the field we want. var contacts = [ - api_output.WhoisRecord.registrant, - api_output.WhoisRecord.administrativeContact, - api_output.WhoisRecord.technicalContact + api_result.WhoisRecord.registrant, + api_result.WhoisRecord.administrativeContact, + api_result.WhoisRecord.technicalContact ]; // return the normalized output as a hash @@ -79,8 +79,8 @@ // these first fields are not displayed // (hence the camelCase, which the user will not see) - 'domainName': api_output.WhoisRecord.domainName, - 'registered': !!api_output.WhoisRecord.registrant, // boolean flag + 'domainName': api_result.WhoisRecord.domainName, + 'registered': !!api_result.WhoisRecord.registrant, // boolean flag // the remaining fields are displayed // (hence the user-friendly capitalization and spaces) @@ -89,11 +89,11 @@ 'Email': get_first_by_key(contacts, 'email'), // trim dates so they are shown without times - 'Last updated': api_output.WhoisRecord.updatedDate - && api_output.WhoisRecord.updatedDate.replace(/^(.*)?\s(.*)?$/, '$1'), + 'Last updated': api_result.WhoisRecord.updatedDate + && api_result.WhoisRecord.updatedDate.replace(/^(.*)?\s(.*)?$/, '$1'), - 'Expires': api_output.WhoisRecord.expiresDate - && api_output.WhoisRecord.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'), + 'Expires': api_result.WhoisRecord.expiresDate + && api_result.WhoisRecord.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'), }; } @@ -122,14 +122,14 @@ } // Data that's shared between the two Spice.add calls. - var get_shared_spice_data = function(api_output) { + var get_shared_spice_data = function(api_result) { return { id: "whois", name: "Whois", meta: { sourceName: "Whois API", sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' - + api_output.domainName + + api_result.domainName + '&outputFormat=json&target=raw' }, templates: { @@ -142,26 +142,26 @@ }; // Show message saying that the domain is available. - var show_available = function(api_output) { - if(is_debug) console.log('api result in show_available', api_output); + var show_available = function(api_result) { + if(is_debug) console.log('api result in show_available', api_result); - var shared_spice_data = get_shared_spice_data(api_output); + var shared_spice_data = get_shared_spice_data(api_result); // add the attributes specific to this template - shared_spice_data.data = api_output; + shared_spice_data.data = api_result; shared_spice_data.templates.options.content = Spice.whois.available; Spice.add(shared_spice_data); }; // Show whois info for the domain using the 'record' template. - var show_whois = function(api_output) { + var show_whois = function(api_result) { - var shared_spice_data = get_shared_spice_data(api_output); + var shared_spice_data = get_shared_spice_data(api_result); // add the attributes specific to this template shared_spice_data.data = { - 'record_data': api_output, + 'record_data': api_result, 'record_keys': ['Registered to', 'Email', 'Last updated', 'Expires'] }; shared_spice_data.templates.options.content = 'record'; From 6d67dc04e30327cfac9998fe09a6251ee3a2fc73 Mon Sep 17 00:00:00 2001 From: b1ake Date: Fri, 11 Jul 2014 19:17:07 +0000 Subject: [PATCH 32/45] Allow leading and trailing spaces and a trailing question mark. Added tests for this as well, which pass. --- lib/DDG/Spice/Whois.pm | 18 ++++++++++++++++-- t/Whois.t | 6 ++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/DDG/Spice/Whois.pm b/lib/DDG/Spice/Whois.pm index e1cbeeda57..145ac83a1f 100644 --- a/lib/DDG/Spice/Whois.pm +++ b/lib/DDG/Spice/Whois.pm @@ -42,8 +42,15 @@ my $whois_keywords_qr = qr/whois|lookup|(?:is\s|)domain|(?:is\s|)available|regis # narrow this spice's query space. # triggers query_raw => - qr/^$url_qr$/, - qr/^$whois_keywords_qr|$whois_keywords_qr$/; + # allow the naked url with leading and trailing spaces + qr/^\s*$url_qr\s*$/, + + # allow the whois keywords at the beginning or end of the string + # with leading or trailing spaces. + # + # if at the end of the string, allow a trailing question mark. + qr/^\s*$whois_keywords_qr + |$whois_keywords_qr[?]?\s*$/x; # API call details for Whois API (http://www.whoisxmlapi.com/) spice to => 'http://www.whoisxmlapi.com/whoisserver/WhoisService?domainName=$1&outputFormat=JSON&callback={{callback}}&username={{ENV{DDG_SPICE_WHOIS_USERNAME}}}&password={{ENV{DDG_SPICE_WHOIS_PASSWORD}}}'; @@ -52,6 +59,13 @@ handle sub { my ($query) = @_; return if !$query; # do not trigger this spice if the query is blank + # trim any leading and trailing spaces + $query = trim($query); + + # remove any trailing question marks, which are allowed + # but can disrupt the regexs + $query =~ s/\?$//; + # parse the URL into its parts my ($subdomains, $domain, $tld, $port, $resource_path) = $query =~ $url_qr; diff --git a/t/Whois.t b/t/Whois.t index 99ff98a96f..6e1e0092f8 100644 --- a/t/Whois.t +++ b/t/Whois.t @@ -76,6 +76,12 @@ ddg_spice_test( # whois keywords should be case insensitive 'hOw To bUy duckduckgo.com' => expected_output_for('duckduckgo.com'), + # leading and trailing spaces should be allowed + ' how to buy duckduckgo.com ' => expected_output_for('duckduckgo.com'), + + # a trailing question mark should be allowed + 'is duckduckgo.com available?' => expected_output_for('duckduckgo.com'), + # whois keywords after a url should trigger 'duckduckgo.com whois' => expected_output_for('duckduckgo.com'), 'duckduckgo.com lookup' => expected_output_for('duckduckgo.com'), From 7f8c31e37f5df192a099c80d1ee77ad7853bfe33 Mon Sep 17 00:00:00 2001 From: b1ake Date: Fri, 11 Jul 2014 19:59:06 +0000 Subject: [PATCH 33/45] Updated API parsing to handle the 'duck.co' case, and also to make a little cleaner. --- share/spice/whois/whois.js | 50 ++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 73e46b121b..6b2b59f2c9 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -7,7 +7,7 @@ // spice callback function env.ddg_spice_whois = function(api_result) { // for debugging - if(is_debug) console.log('api_result:', api_result); + if(is_debug) console.log('in start of JS, api_result =', api_result); // Check for API error and exit early if found // (with error message when in debug mode) @@ -21,7 +21,7 @@ api_result = normalize_api_result(api_result); // is the domain available? - var is_avail = is_domain_available(api_result); + var is_avail = api_result.available; // if the domain isn't available, do we want to show // whois information? @@ -43,34 +43,34 @@ }; - // Returns whether the domain is available, - // based on the API result that was returned. - var is_domain_available = function(api_result) { - return !api_result['registered']; - }; - // Returns whether we should show whois data if this // domain is not available. var is_whois_query = function(query) { - - // for debugging - if(is_debug) console.log('in is_whois_query, query =', query); - // show whois results except when the query contains only the domain // and no other keywords, which we test by looking for a space in the query. return /\s/.test($.trim(query)); }; + // parse the api response into a standard format var normalize_api_result = function(api_result) { + // use only the 'WhoisRecord' portion, because that's where + // all the data is stored. + api_result = api_result.WhoisRecord; + + // sometimes the data is nested inside the 'registryData' object + if(api_result.registryData && api_result.registryData.registrant) { + api_result = api_result.registryData; + } + // store the domain's various contacts in an array. // // we'll iterate through this array in order, using // info from the first contact that contains the field we want. var contacts = [ - api_result.WhoisRecord.registrant, - api_result.WhoisRecord.administrativeContact, - api_result.WhoisRecord.technicalContact + api_result.registrant, + api_result.administrativeContact, + api_result.technicalContact ]; // return the normalized output as a hash @@ -79,8 +79,8 @@ // these first fields are not displayed // (hence the camelCase, which the user will not see) - 'domainName': api_result.WhoisRecord.domainName, - 'registered': !!api_result.WhoisRecord.registrant, // boolean flag + 'domainName': api_result.domainName, + 'available': is_domain_available(api_result), // the remaining fields are displayed // (hence the user-friendly capitalization and spaces) @@ -89,15 +89,20 @@ 'Email': get_first_by_key(contacts, 'email'), // trim dates so they are shown without times - 'Last updated': api_result.WhoisRecord.updatedDate - && api_result.WhoisRecord.updatedDate.replace(/^(.*)?\s(.*)?$/, '$1'), + 'Last updated': api_result.updatedDate + && api_result.updatedDate.replace(/^(.*)?\s(.*)?$/, '$1'), - 'Expires': api_result.WhoisRecord.expiresDate - && api_result.WhoisRecord.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'), + 'Expires': api_result.expiresDate + && api_result.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'), }; } + // Returns whether the domain is registered to someone, based on the API result. + var is_domain_available = function(api_result) { + return !api_result.registrant; + }; + // Searches an array of objects for the first value // at the specified key. var get_first_by_key = function(arr, key) { @@ -143,8 +148,6 @@ // Show message saying that the domain is available. var show_available = function(api_result) { - if(is_debug) console.log('api result in show_available', api_result); - var shared_spice_data = get_shared_spice_data(api_result); // add the attributes specific to this template @@ -156,7 +159,6 @@ // Show whois info for the domain using the 'record' template. var show_whois = function(api_result) { - var shared_spice_data = get_shared_spice_data(api_result); // add the attributes specific to this template From 576a0d891897f1a0b59a62ae255091718d28d082 Mon Sep 17 00:00:00 2001 From: b1ake Date: Tue, 15 Jul 2014 22:05:51 +0000 Subject: [PATCH 34/45] Trying to update styles based on @morast's feedback. Not working for some reason though. --- share/spice/whois/whois.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 share/spice/whois/whois.css diff --git a/share/spice/whois/whois.css b/share/spice/whois/whois.css new file mode 100644 index 0000000000..e5907bc4f8 --- /dev/null +++ b/share/spice/whois/whois.css @@ -0,0 +1,12 @@ +.record__cell__key { + padding-right: 32px; +} + +.zci__more-at { + padding-top: 1em; +} + +.zci.is-active { + padding-bottom: 1.5em; + padding-top: 1.5em; +} \ No newline at end of file From 0889d8223c41f73e443c12f0040b6e32fd86bdf1 Mon Sep 17 00:00:00 2001 From: b1ake Date: Tue, 15 Jul 2014 22:44:54 +0000 Subject: [PATCH 35/45] Fixed CSS changes, they are now working. --- share/spice/whois/whois.css | 14 +++++++------- share/spice/whois/whois.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/share/spice/whois/whois.css b/share/spice/whois/whois.css index e5907bc4f8..fea957cc48 100644 --- a/share/spice/whois/whois.css +++ b/share/spice/whois/whois.css @@ -1,12 +1,12 @@ -.record__cell__key { - padding-right: 32px; +.zci--whois { + padding-bottom: 1.5em !important; + padding-top: 1.5em !important; } -.zci__more-at { - padding-top: 1em; +.zci--whois .record__cell__key { + padding-right: 32px; } -.zci.is-active { - padding-bottom: 1.5em; - padding-top: 1.5em; +.zci--whois .zci__more-at { + padding-top: 1em; } \ No newline at end of file diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 6b2b59f2c9..8f4710fbc8 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -2,7 +2,7 @@ "use strict"; // turns on/off debugging output - var is_debug = false; + var is_debug = true; // spice callback function env.ddg_spice_whois = function(api_result) { From e7d117c818bb5d6f725802cb867e26dd547befdc Mon Sep 17 00:00:00 2001 From: b1ake Date: Tue, 15 Jul 2014 23:06:28 +0000 Subject: [PATCH 36/45] Fix domain availability check so that 'zaahir.ca' works, and update output to make sense in that case. --- share/spice/whois/whois.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 8f4710fbc8..c104733236 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -20,6 +20,8 @@ // normalize the api output api_result = normalize_api_result(api_result); + if(is_debug) console.log('normalized api_result:', api_result); + // is the domain available? var is_avail = api_result.available; @@ -59,7 +61,10 @@ api_result = api_result.WhoisRecord; // sometimes the data is nested inside the 'registryData' object - if(api_result.registryData && api_result.registryData.registrant) { + if(!api_result.createdDate + && api_result.registryData + && api_result.registryData.createdDate) { + api_result = api_result.registryData; } @@ -84,23 +89,24 @@ // the remaining fields are displayed // (hence the user-friendly capitalization and spaces) - + 'Status': is_domain_available(api_result) ? 'Available' : 'Registered', 'Registered to': get_first_by_key(contacts, 'name'), 'Email': get_first_by_key(contacts, 'email'), // trim dates so they are shown without times + // (if no time was found, the replace() call will return undef, + // so we need to fallback to the original string) 'Last updated': api_result.updatedDate - && api_result.updatedDate.replace(/^(.*)?\s(.*)?$/, '$1'), + && api_result.updatedDate.replace(/^(.*)?\s(.*)?$/, '$1'), 'Expires': api_result.expiresDate - && api_result.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'), - + && api_result.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'), }; } // Returns whether the domain is registered to someone, based on the API result. var is_domain_available = function(api_result) { - return !api_result.registrant; + return api_result.dataError && api_result.dataError === 'MISSING_WHOIS_DATA'; }; // Searches an array of objects for the first value @@ -164,7 +170,7 @@ // add the attributes specific to this template shared_spice_data.data = { 'record_data': api_result, - 'record_keys': ['Registered to', 'Email', 'Last updated', 'Expires'] + 'record_keys': ['Status', 'Registered to', 'Email', 'Last updated', 'Expires'] }; shared_spice_data.templates.options.content = 'record'; From 53136d54b73d5487167873019fe70cd5172fe554 Mon Sep 17 00:00:00 2001 From: b1ake Date: Tue, 15 Jul 2014 23:08:47 +0000 Subject: [PATCH 37/45] Removed italics from domain available template, and tweaked styling to make the top line stand out. --- share/spice/whois/available.handlebars | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/spice/whois/available.handlebars b/share/spice/whois/available.handlebars index 31225b9c8a..084e774b09 100644 --- a/share/spice/whois/available.handlebars +++ b/share/spice/whois/available.handlebars @@ -1,3 +1,3 @@ -The domain {{domainName}} may be available!
-To register, try Domainr, NameCheap or 101Domains
+The domain '{{domainName}}' may be available!
+To register, try Domainr, NameCheap or 101Domains

\ No newline at end of file From 006c402301f62bba215fca7736aad921fcc4a360 Mon Sep 17 00:00:00 2001 From: b1ake Date: Wed, 16 Jul 2014 14:36:12 +0000 Subject: [PATCH 38/45] Better handling of errors and missing data. --- share/spice/whois/whois.js | 43 +++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index c104733236..8461451078 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -2,26 +2,27 @@ "use strict"; // turns on/off debugging output - var is_debug = true; + var is_debug = false; // spice callback function - env.ddg_spice_whois = function(api_result) { + env.ddg_spice_whois = function(raw_api_result) { + // for debugging - if(is_debug) console.log('in start of JS, api_result =', api_result); + if(is_debug) console.log('in start of JS, raw_api_result:', raw_api_result); + + // normalize the api output + var api_result = normalize_api_result(raw_api_result); + + if(is_debug) console.log('normalized api_result:', api_result || 'empty'); // Check for API error and exit early if found // (with error message when in debug mode) - if (!api_result || api_result.error || !api_result.WhoisRecord) { - if(is_debug) console.log("Error with whois API. api_result:", api_result || 'undefined'); + if (!api_result) { + if(is_debug) console.log('Error with whois API. raw_api_result:', raw_api_result || 'empty', ', normalized api_result:', api_result || 'empty'); return Spice.failed('whois'); } - // normalize the api output - api_result = normalize_api_result(api_result); - - if(is_debug) console.log('normalized api_result:', api_result); - // is the domain available? var is_avail = api_result.available; @@ -56,6 +57,9 @@ // parse the api response into a standard format var normalize_api_result = function(api_result) { + // return nothing if no api_result, if error, or if WhoisRecord object is missing + if(!api_result || api_result.error || !api_result.WhoisRecord) return; + // use only the 'WhoisRecord' portion, because that's where // all the data is stored. api_result = api_result.WhoisRecord; @@ -79,7 +83,7 @@ ]; // return the normalized output as a hash - return { + var normalized = { // these first fields are not displayed // (hence the camelCase, which the user will not see) @@ -89,6 +93,7 @@ // the remaining fields are displayed // (hence the user-friendly capitalization and spaces) + 'Status': is_domain_available(api_result) ? 'Available' : 'Registered', 'Registered to': get_first_by_key(contacts, 'name'), 'Email': get_first_by_key(contacts, 'email'), @@ -102,6 +107,16 @@ 'Expires': api_result.expiresDate && api_result.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'), }; + + // return nothing if all key whois data is missing + if( !normalized['Registered to'] + && !normalized['Email'] + && !normalized['Last updated'] + && !normalized['Expires']) { + return; + } + + return normalized; } // Returns whether the domain is registered to someone, based on the API result. @@ -112,10 +127,10 @@ // Searches an array of objects for the first value // at the specified key. var get_first_by_key = function(arr, key) { - if(!arr || arr.length == 0) return null; + if(!arr || arr.length == 0) return; // find the first object in the array that has a non-empty value at the key - var first = null; + var first; $.each(arr, function(index, obj) { // get the value at the specified key // (which could be undefined) @@ -128,7 +143,7 @@ } }); - // return first, which could still be null + // return first, which could still be empty return first; } From 5dc69dffbf48575a443357d19a6b36d25b89cbb0 Mon Sep 17 00:00:00 2001 From: b1ake Date: Mon, 21 Jul 2014 17:54:10 +0000 Subject: [PATCH 39/45] Add tests that use trigger words without a URL (and so shouldn't trigger the spice). --- t/Whois.t | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/t/Whois.t b/t/Whois.t index 6e1e0092f8..2aab842241 100644 --- a/t/Whois.t +++ b/t/Whois.t @@ -97,17 +97,26 @@ ddg_spice_test( # whois keywords without a url should not trigger 'whois' => undef, + 'whois tom cruise' => undef, 'lookup' => undef, + "lookup someone's phone number" => undef, 'domain' => undef, + 'eminent domain' => undef, 'is domain' => undef, 'available' => undef, + 'fios available' => undef, 'is available' => undef, + 'is water available' => undef, 'register' => undef, + 'register to vote' => undef, 'owner' => undef, - 'owner' => undef, + 'owner of seattle mariners' => undef, 'who owns' => undef, + 'who owns 20 Paoli Pike' => undef, 'buy' => undef, + 'buy stocks online' => undef, 'how to buy' => undef, + 'how to buy a TV' => undef, ); From 57b9c6030c50f37028845d610b2adb60d9e09b54 Mon Sep 17 00:00:00 2001 From: b1ake Date: Wed, 23 Jul 2014 21:25:02 +0000 Subject: [PATCH 40/45] Update check for registry data to look for expiration or created data, so that 'is transistor.io available' correclty displays whois data. --- share/spice/whois/whois.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 8461451078..66464fd76f 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -67,7 +67,7 @@ // sometimes the data is nested inside the 'registryData' object if(!api_result.createdDate && api_result.registryData - && api_result.registryData.createdDate) { + && (api_result.registryData.createdDate || api_result.registryData.expiresDate) ){ api_result = api_result.registryData; } From 05b1cd0e1196dacec090f20fe92fc4a415c4fc0c Mon Sep 17 00:00:00 2001 From: b1ake Date: Wed, 23 Jul 2014 21:45:18 +0000 Subject: [PATCH 41/45] Added the keySpacing flag plus a comment next to my custom CSS, which we should be able to remove once the keySpacing code is merged in. --- share/spice/whois/whois.css | 3 +++ share/spice/whois/whois.js | 1 + 2 files changed, 4 insertions(+) diff --git a/share/spice/whois/whois.css b/share/spice/whois/whois.css index fea957cc48..be0f717d50 100644 --- a/share/spice/whois/whois.css +++ b/share/spice/whois/whois.css @@ -1,3 +1,6 @@ +/* ONCE THE KEYSPACING FLAG IS MERGED INTO THE REPO, + none of this custom css should be needed. */ + .zci--whois { padding-bottom: 1.5em !important; padding-top: 1.5em !important; diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 66464fd76f..5e4df27cc3 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -188,6 +188,7 @@ 'record_keys': ['Status', 'Registered to', 'Email', 'Last updated', 'Expires'] }; shared_spice_data.templates.options.content = 'record'; + shared_spice_data.templates.options.keySpacing = true; Spice.add(shared_spice_data); }; From 3bb330022605d4af959ac105e6a8cef9175a7702 Mon Sep 17 00:00:00 2001 From: b1ake Date: Fri, 25 Jul 2014 17:05:40 +0000 Subject: [PATCH 42/45] Remove custom CSS now that we're using the keySpacing flag. --- share/spice/whois/whois.css | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 share/spice/whois/whois.css diff --git a/share/spice/whois/whois.css b/share/spice/whois/whois.css deleted file mode 100644 index be0f717d50..0000000000 --- a/share/spice/whois/whois.css +++ /dev/null @@ -1,15 +0,0 @@ -/* ONCE THE KEYSPACING FLAG IS MERGED INTO THE REPO, - none of this custom css should be needed. */ - -.zci--whois { - padding-bottom: 1.5em !important; - padding-top: 1.5em !important; -} - -.zci--whois .record__cell__key { - padding-right: 32px; -} - -.zci--whois .zci__more-at { - padding-top: 1em; -} \ No newline at end of file From 16bf7598ef5cccbb8cf77eaffbfe6b5e5c382407 Mon Sep 17 00:00:00 2001 From: b1ake Date: Tue, 2 Sep 2014 20:55:27 +0000 Subject: [PATCH 43/45] Replace tabs iwth spaces. --- lib/DDG/Spice/Whois.pm | 6 +- share/spice/whois/whois.js | 268 ++++++++++++++++++------------------- 2 files changed, 137 insertions(+), 137 deletions(-) diff --git a/lib/DDG/Spice/Whois.pm b/lib/DDG/Spice/Whois.pm index 145ac83a1f..afc9088d42 100644 --- a/lib/DDG/Spice/Whois.pm +++ b/lib/DDG/Spice/Whois.pm @@ -94,9 +94,9 @@ handle sub { # trying to nav to the URL they typed. # return if $is_naked_domain - && ( (defined $subdomains && $subdomains !~ /^www.$/) - || (defined $port && $port ne '') - || (defined $resource_path && $resource_path ne '')); + && ( (defined $subdomains && $subdomains !~ /^www.$/) + || (defined $port && $port ne '') + || (defined $resource_path && $resource_path ne '')); # return the combined domain + tld (after adding a period in between) return lc "$domain.$tld"; diff --git a/share/spice/whois/whois.js b/share/spice/whois/whois.js index 5e4df27cc3..e43d07d4ab 100644 --- a/share/spice/whois/whois.js +++ b/share/spice/whois/whois.js @@ -10,187 +10,187 @@ // for debugging if(is_debug) console.log('in start of JS, raw_api_result:', raw_api_result); - // normalize the api output - var api_result = normalize_api_result(raw_api_result); + // normalize the api output + var api_result = normalize_api_result(raw_api_result); - if(is_debug) console.log('normalized api_result:', api_result || 'empty'); + if(is_debug) console.log('normalized api_result:', api_result || 'empty'); - // Check for API error and exit early if found - // (with error message when in debug mode) - if (!api_result) { - if(is_debug) console.log('Error with whois API. raw_api_result:', raw_api_result || 'empty', ', normalized api_result:', api_result || 'empty'); + // Check for API error and exit early if found + // (with error message when in debug mode) + if (!api_result) { + if(is_debug) console.log('Error with whois API. raw_api_result:', raw_api_result || 'empty', ', normalized api_result:', api_result || 'empty'); - return Spice.failed('whois'); - } + return Spice.failed('whois'); + } // is the domain available? - var is_avail = api_result.available; - - // if the domain isn't available, do we want to show - // whois information? - var is_whois_allowed = is_whois_query(DDG.get_query()); - - // for debugging - if(is_debug) console.log("is_avail:", is_avail, "is_whois_allowed:", is_whois_allowed); - - // decide which template to show, if any - if(is_avail) { - // show message saying the domain is available - show_available(api_result); - } else if(is_whois_allowed) { - // show whois info for the domain - show_whois(api_result); - } else { - // by default, show nothing - } + var is_avail = api_result.available; + + // if the domain isn't available, do we want to show + // whois information? + var is_whois_allowed = is_whois_query(DDG.get_query()); + + // for debugging + if(is_debug) console.log("is_avail:", is_avail, "is_whois_allowed:", is_whois_allowed); + + // decide which template to show, if any + if(is_avail) { + // show message saying the domain is available + show_available(api_result); + } else if(is_whois_allowed) { + // show whois info for the domain + show_whois(api_result); + } else { + // by default, show nothing + } }; // Returns whether we should show whois data if this // domain is not available. var is_whois_query = function(query) { - // show whois results except when the query contains only the domain - // and no other keywords, which we test by looking for a space in the query. - return /\s/.test($.trim(query)); + // show whois results except when the query contains only the domain + // and no other keywords, which we test by looking for a space in the query. + return /\s/.test($.trim(query)); }; // parse the api response into a standard format var normalize_api_result = function(api_result) { - // return nothing if no api_result, if error, or if WhoisRecord object is missing - if(!api_result || api_result.error || !api_result.WhoisRecord) return; - - // use only the 'WhoisRecord' portion, because that's where - // all the data is stored. - api_result = api_result.WhoisRecord; - - // sometimes the data is nested inside the 'registryData' object - if(!api_result.createdDate - && api_result.registryData - && (api_result.registryData.createdDate || api_result.registryData.expiresDate) ){ - - api_result = api_result.registryData; - } - - // store the domain's various contacts in an array. - // - // we'll iterate through this array in order, using - // info from the first contact that contains the field we want. - var contacts = [ - api_result.registrant, - api_result.administrativeContact, - api_result.technicalContact - ]; - - // return the normalized output as a hash - var normalized = { - - // these first fields are not displayed - // (hence the camelCase, which the user will not see) - - 'domainName': api_result.domainName, - 'available': is_domain_available(api_result), - - // the remaining fields are displayed - // (hence the user-friendly capitalization and spaces) - - 'Status': is_domain_available(api_result) ? 'Available' : 'Registered', - 'Registered to': get_first_by_key(contacts, 'name'), - 'Email': get_first_by_key(contacts, 'email'), - - // trim dates so they are shown without times - // (if no time was found, the replace() call will return undef, - // so we need to fallback to the original string) - 'Last updated': api_result.updatedDate - && api_result.updatedDate.replace(/^(.*)?\s(.*)?$/, '$1'), - - 'Expires': api_result.expiresDate - && api_result.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'), - }; - - // return nothing if all key whois data is missing - if( !normalized['Registered to'] - && !normalized['Email'] - && !normalized['Last updated'] - && !normalized['Expires']) { - return; - } - - return normalized; + // return nothing if no api_result, if error, or if WhoisRecord object is missing + if(!api_result || api_result.error || !api_result.WhoisRecord) return; + + // use only the 'WhoisRecord' portion, because that's where + // all the data is stored. + api_result = api_result.WhoisRecord; + + // sometimes the data is nested inside the 'registryData' object + if(!api_result.createdDate + && api_result.registryData + && (api_result.registryData.createdDate || api_result.registryData.expiresDate) ){ + + api_result = api_result.registryData; + } + + // store the domain's various contacts in an array. + // + // we'll iterate through this array in order, using + // info from the first contact that contains the field we want. + var contacts = [ + api_result.registrant, + api_result.administrativeContact, + api_result.technicalContact + ]; + + // return the normalized output as a hash + var normalized = { + + // these first fields are not displayed + // (hence the camelCase, which the user will not see) + + 'domainName': api_result.domainName, + 'available': is_domain_available(api_result), + + // the remaining fields are displayed + // (hence the user-friendly capitalization and spaces) + + 'Status': is_domain_available(api_result) ? 'Available' : 'Registered', + 'Registered to': get_first_by_key(contacts, 'name'), + 'Email': get_first_by_key(contacts, 'email'), + + // trim dates so they are shown without times + // (if no time was found, the replace() call will return undef, + // so we need to fallback to the original string) + 'Last updated': api_result.updatedDate + && api_result.updatedDate.replace(/^(.*)?\s(.*)?$/, '$1'), + + 'Expires': api_result.expiresDate + && api_result.expiresDate.replace(/^(.*)?\s(.*)?$/, '$1'), + }; + + // return nothing if all key whois data is missing + if( !normalized['Registered to'] + && !normalized['Email'] + && !normalized['Last updated'] + && !normalized['Expires']) { + return; + } + + return normalized; } // Returns whether the domain is registered to someone, based on the API result. var is_domain_available = function(api_result) { - return api_result.dataError && api_result.dataError === 'MISSING_WHOIS_DATA'; + return api_result.dataError && api_result.dataError === 'MISSING_WHOIS_DATA'; }; // Searches an array of objects for the first value // at the specified key. var get_first_by_key = function(arr, key) { - if(!arr || arr.length == 0) return; - - // find the first object in the array that has a non-empty value at the key - var first; - $.each(arr, function(index, obj) { - // get the value at the specified key - // (which could be undefined) - var value = obj && obj[key]; - - // update the first var if the value is truthy - // and first hasn't already been found - if(!first && value) { - first = value; - } - }); - - // return first, which could still be empty - return first; + if(!arr || arr.length == 0) return; + + // find the first object in the array that has a non-empty value at the key + var first; + $.each(arr, function(index, obj) { + // get the value at the specified key + // (which could be undefined) + var value = obj && obj[key]; + + // update the first var if the value is truthy + // and first hasn't already been found + if(!first && value) { + first = value; + } + }); + + // return first, which could still be empty + return first; } // Data that's shared between the two Spice.add calls. var get_shared_spice_data = function(api_result) { - return { + return { id: "whois", name: "Whois", meta: { - sourceName: "Whois API", - sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' - + api_result.domainName - + '&outputFormat=json&target=raw' + sourceName: "Whois API", + sourceUrl: 'http://www.whoisxmlapi.com/whois-api-doc.php#whoisserver/WhoisService?rid=2&domainName=' + + api_result.domainName + + '&outputFormat=json&target=raw' }, templates: { - group: 'base', - options:{ - moreAt: true - } - } - }; + group: 'base', + options:{ + moreAt: true + } + } + }; }; // Show message saying that the domain is available. var show_available = function(api_result) { - var shared_spice_data = get_shared_spice_data(api_result); + var shared_spice_data = get_shared_spice_data(api_result); - // add the attributes specific to this template + // add the attributes specific to this template shared_spice_data.data = api_result; - shared_spice_data.templates.options.content = Spice.whois.available; + shared_spice_data.templates.options.content = Spice.whois.available; - Spice.add(shared_spice_data); + Spice.add(shared_spice_data); }; // Show whois info for the domain using the 'record' template. var show_whois = function(api_result) { - var shared_spice_data = get_shared_spice_data(api_result); + var shared_spice_data = get_shared_spice_data(api_result); - // add the attributes specific to this template - shared_spice_data.data = { - 'record_data': api_result, - 'record_keys': ['Status', 'Registered to', 'Email', 'Last updated', 'Expires'] - }; - shared_spice_data.templates.options.content = 'record'; - shared_spice_data.templates.options.keySpacing = true; + // add the attributes specific to this template + shared_spice_data.data = { + 'record_data': api_result, + 'record_keys': ['Status', 'Registered to', 'Email', 'Last updated', 'Expires'] + }; + shared_spice_data.templates.options.content = 'record'; + shared_spice_data.templates.options.keySpacing = true; - Spice.add(shared_spice_data); + Spice.add(shared_spice_data); }; From 58ebd766c33ab7a4189c746baae6ddd45f1bd2b6 Mon Sep 17 00:00:00 2001 From: jagtalon Date: Thu, 25 Sep 2014 21:30:55 +0000 Subject: [PATCH 44/45] Whois: Add CSS --- share/spice/whois/whois.css | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 share/spice/whois/whois.css diff --git a/share/spice/whois/whois.css b/share/spice/whois/whois.css new file mode 100644 index 0000000000..e74a300b5c --- /dev/null +++ b/share/spice/whois/whois.css @@ -0,0 +1,8 @@ +.zci--whois table { + table-layout: fixed; + width: 100%; +} + +.zci--whois table td { + word-wrap: break-word; +} \ No newline at end of file From 460209db07acfbc1104bb36011f8af9508d963a8 Mon Sep 17 00:00:00 2001 From: jagtalon Date: Thu, 25 Sep 2014 21:59:09 +0000 Subject: [PATCH 45/45] Whois: Only apply CSS at a certain size. --- share/spice/whois/whois.css | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/share/spice/whois/whois.css b/share/spice/whois/whois.css index e74a300b5c..980a088bdd 100644 --- a/share/spice/whois/whois.css +++ b/share/spice/whois/whois.css @@ -1,8 +1,11 @@ -.zci--whois table { - table-layout: fixed; - width: 100%; -} -.zci--whois table td { - word-wrap: break-word; +@media (max-width: 600px) { + .zci--whois table { + table-layout: fixed; + width: 100%; + } + + .zci--whois table td { + word-wrap: break-word; + } } \ No newline at end of file