Source: plugins/openweathermap/owm/jquery.owm.js

/**
 * Plugin: jquery.OWM
 *
 * jQuery plugin to display data from OpenWeatherMap.
 *
 * Version: 0.0.1
 * Copyright (c) 2015, Stefan Borchert
 *
 * Licensed under the GPLv3 license.
**/

var jOWM = jOWM || {};

(function ($) {

  $.fn.openweathermap = function(options, fn) {

    // Set plugin defaults.
    var defaults = {
      // Base URL to service.
      baseURL: 'http://api.openweathermap.org/data/2.5/',
      // Number of items to show detailed data for (0..4).
      detailItems: 4,
      // Number of items in forecast (0..16).
      forecastItems: 5,
      // Include today in forecast?
      forecastToday: true,
      // Default units.
      units: 'metric',
      // Type ('exact', 'like').
      type: 'like',
      // Refresh interval in minutes.
      refresh: 30,
      // App-ID needed to access the service.
      appid: ''
    };

    var options = $.extend(defaults, options);

    // Sanitize options.
    options.detailItems = parseInt(options.detailItems, 10);
    if (options.detailItems < 0) {
      options.detailItems = 0;
    }
    if (options.detailItems > 4) {
      options.detailItems = 4;
    }
    options.forecastItems = parseInt(options.forecastItems, 10);
    if (options.forecastItems < 0) {
      options.forecastItems = 0;
    }
    if (options.forecastItems > 16) {
      options.forecastItems = 16;
    }

    // extend locales by German and French
    Date.ext.locales['de'] = {
      a: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
      A: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
      b: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
      B: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
    };

    return this.each(function(i, e) {
      $element = $(e);

      if (!$element.hasClass('jowm')) {
        $element.addClass('jowm');
      }

      // Process element.
      _process(e, options);

    });

  };

  var _process = function (e, options) {
    var html = '';
    var paramsDefault = _parametersFromOptions(options);
    var currentURL = options.baseURL + 'forecast?' + paramsDefault.join('&');
    var forecastItems = paramsDefault;
    // Limit results to specified number of days.
    forecastItems.push('cnt=' + options.forecastItems);
    var forecastURL = options.baseURL + 'forecast/daily?' + forecastItems.join('&');

    if ($('ul.detailed', $(e)).length === 0) {
      $('<ul>')
        .addClass('detailed')
        .addClass('clearfix')
        .appendTo($(e));
    }
    if ($('ul.forecast', $(e)).length === 0) {
      $('<ul>')
        .addClass('forecast')
        .addClass('clearfix')
        .appendTo($(e));
    }

    // Load location data.
    $.getJSON(options.baseURL + 'weather?' + paramsDefault.join('&'), function (data) {
      if (data.cod == 200) {
        // Load sunrise/sunset.
        options.sunrise = data.sys.sunrise;
        options.sunset = data.sys.sunset;

        // Fetch data for detailed items.
        _processDataDetailed(e, currentURL, options);
        // Fetch data for forecast items.
        _processDataForecast(e, forecastURL, options);
      }
      else {
        $(e).after('<!-- Failed to fetch detailed weather data. -->');
      }
    });

  }

  /**
   * Process detailed weather data and generate output.
   */
  var _processDataDetailed = function (e, url, options) {
    // Clear old markup.
    $('ul.detailed', $(e)).html('');
    if (options.detailItems === 0) {
      // Detailed items are disabled.
      return;
    }
    $.getJSON(url, function (data) {
      if (data.cod == 200) {
        var dataItems = data.list.slice(0, options.detailItems);
        $.each(dataItems, function (index, elem) {
          $item = $('<li>');
          if (elem.dt < options.sunrise || elem.dt > options.sunset) {
            $item.addClass('night');
          }
          if (index === 0) {
            $item.addClass('first');
          }
          if (index === (dataItems.length - 1)) {
            $item.addClass('last');
          }
          $item.html(jOWM.theme('weatherDetailItem', elem, options));
          $item.appendTo($('ul.detailed', $(e)));
        });
      }
      else {
        $(e).after('<!-- Failed to fetch detailed weather data. -->');
      }
    });
  }

  /**
   * Process forecast weather data and generate output.
   */
  var _processDataForecast = function (e, url, options) {
    // Clear old markup.
    $('ul.forecast', $(e)).html('');
    if (options.forecastItems === 0) {
      // Forecast is disabled.
      return;
    }
    $.getJSON(url, function (data) {
      if (data.cod == 200) {
        var dataItems = data.list;
        if (!options.forecastToday) {
          // Remove first item from list.
          dataItems.shift();
        }
        $.each(dataItems, function (index, elem) {
          $item = $('<li>');
          if (index === 0) {
            $item.addClass('first');
          }
          if (index === (dataItems.length - 1)) {
            $item.addClass('last');
          }
          $item.html(jOWM.theme('weatherForecastItem', elem, options));
          $item.appendTo($('ul.forecast', $(e)));
        });
      }
      else {
        $(e).after('<!-- Failed to fetch forecast weather data. -->');
      }
    });
  }

  /**
   * Helper function to create URL parameters from options.
   *
   * @param options
   *   List of widget options.
   */
  var _parametersFromOptions = function(options) {
    var items = [];
    if (options.hasOwnProperty('lang')) {
      items.push('lang=' + options.lang);
    }
    if (options.hasOwnProperty('q')) {
      items.push('q=' + options.q);
    }
    else if (options.hasOwnProperty('lat') && options.hasOwnProperty('lon')) {
      items.push('lat=' + options.lat);
      items.push('lon=' + options.lon);
    }
    if (options.hasOwnProperty('units')) {
      items.push('units=' + options.units);
    }
    if (options.hasOwnProperty('type')) {
      items.push('type=' + options.type);
    }
    if (options.hasOwnProperty('appid')) {
      items.push('appid=' + options.appid);
    }
    return items;
  }

  /**
   * Generic theme function.
   */
  jOWM.theme = function (func) {
    var args = Array.prototype.slice.apply(arguments, [1]);

    return (jOWM.theme[func] || jOWM.theme.prototype[func]).apply(this, args);
  };

  /**
   * Default theme function for weather temperature.
   */
  jOWM.theme.prototype.weatherTemperature = function (temperature, precision, suffix) {
    suffix = suffix || '°';
    return parseFloat(temperature).toFixed(precision) + suffix;
  }

  /**
   * Default theme function for detailed weather item.
   */
  jOWM.theme.prototype.weatherDetailItem = function (data, options) {
    var weather = data.weather[0];
    var temperature = data.main;
    var d = new Date(data.dt * 1000); // We need ms, not s!
    d.locale = options.lang;

    var output = '<div class="weather-detailed weather-' + weather.id + ' clearfix">';
    output += ' <div class="weather">';
    output += '  <span class="weather-icon" data-weather-text="' + weather.description + '" data-weather-code="' + weather.id + '"></span>';
    output += ' </div>';
    output += ' <div class="temperature">' + jOWM.theme('weatherTemperature', temperature.temp, 1) + '</div>';
    output += '</div>';
    return output;
  };

  /**
   * Default theme function for forecast weather item.
   */
  jOWM.theme.prototype.weatherForecastItem = function (data, options) {
    var weather = data.weather[0];
    var temperature = data.temp;
    var d = new Date(data.dt * 1000); // We need ms, not s!
    d.locale = options.lang;

    var output = '<div class="weather-forecast weather-' + weather.id + ' clearfix">';
    output += ' <div class="day">' + d.strftime('%a') + '</div>';
    output += ' <div class="weather-icon" data-weather-text="' + weather.description + '" data-weather-code="' + weather.id + '"></div>';
    output += ' <div class="temperature high">' + jOWM.theme('weatherTemperature', temperature.max, 0, ' °C') + '</div>';
    output += ' <div class="temperature low">' + jOWM.theme('weatherTemperature', temperature.max, 0, ' °C') + '</div>';
    output += '</div>';
    return output;
  };

})(jQuery);