function Module (callback) { return callback() } /** * Show exceptions from Ajax responders. */ Ajax.Responders.register ({ onException: function (requester, exception) { console && console.info (requester, exception) } }) /** * Element extensions */ Element.addMethods ({ /** * Switches element between two class names. */ switchClassNames: function (element, bool, on_class, off_class) { if (bool) { element.removeClassName (off_class) if (on_class) element.addClassName (on_class) } else { element.removeClassName (on_class) if (off_class) element.addClassName (off_class) } return element }, setPosition: function (element, x, y) { element.style.left = x + 'px' element.style.top = y + 'px' }, /** * Hides element using visibility: 'hidden' and absolute positioning. */ visibilityHide: function (element) { return element.setStyle ({ visibility: 'hidden' }) }, /** * Shows back element hidden with visibilityHide. */ visibilityShow: function (element) { return element.setStyle ({ visibility: '' }) } }) /** * Helper for input default text autoclearing on focus. */ var Autoclear = Class.create ({ initialize: function (element, default_value) { this.element = $(element) this.focus_observer = function() { if (this.value == (this.getAttribute ('default') || this.defaultValue)) this.value = '' } this.blur_observer = function() { if (!this.value) this.value = this.getAttribute ('default') || this.defaultValue } this.keyup_observer = function (event) { if (event.keyCode == 27) this.value = '' } this.element.observe ('focus', this.focus_observer) this.element.observe ('blur', this.blur_observer) this.element.observe ('keyup', this.keyup_observer) }, destroy: function() { this.element.stopObserving ('focus', this.focus_observer) this.element.stopObserving ('blur', this.blur_observer) this.element.stopObserving ('keyup', this.keyup_observer) } }) /** * Helper for diff components. */ var DifferenceRadios = Class.create ({ initialize: function (radios_1, radios_2, options) { this.options = Object.extend ({ dont_select: false, hide_disabled: true }, options) this.radios_1 = radios_1.collect (function (e) { return $(e) }) this.radios_2 = radios_2.collect (function (e) { return $(e) }) this.radios_1.each (function (radio, index) { radio.difference_radios_observer = radio.observe ('click', this.__radio_1_click.bind (this)) radio.index = index radio.setAttribute ('t', 1) }.bind (this)) this.radios_2.each (function (radio, index) { radio.difference_radios_observer = radio.observe ('click', this.__radio_2_click.bind (this)) radio.index = index radio.setAttribute ('t', 2) }.bind (this)) if (!this.options.dont_select) { this.radios_1[1].click() this.radios_2[0].click() } }, destroy: function() { this.radios_1.concat (this.radios_2).each (function (radio) { radio.stopObserving (radio.difference_radios_observer) }) delete this.radios_1 delete this.radios_2 }, selected_1: function() { return this.radios_1.find (function (radio) { return radio.checked }) }, selected_2: function() { return this.radios_2.find (function (radio) { return radio.checked }) }, __radio_1_click: function (event) { // Hide following radios starting from selected radio-1: var p = event.element().index var a = this.radios_2.slice (0, p).invoke ('enable') var b = this.radios_2.slice (p).invoke ('disable') if (this.options.hide_disabled) this.__hide_disabled() }, __radio_2_click: function (event) { // Hide previous radios until selected radio-2: var p = event.element().index var a = this.radios_1.slice (0, p+1).invoke ('disable') var b = this.radios_1.slice (p+1).invoke ('enable') if (this.options.hide_disabled) this.__hide_disabled() }, __hide_disabled: function() { this.radios_1.concat (this.radios_2).each (function (radio) { radio.disabled? radio.visibilityHide() : radio.visibilityShow() }) } }) /** * Cookie manipulation functions. */ var Cookie = Class.create() Object.extend (Cookie, { get: function (name) { var start = document.cookie.indexOf (name + "=") var len = start + name.length + 1 if ((!start) && (name != document.cookie.substring (0, name.length))) return null if (start == -1) return null var end = document.cookie.indexOf (';', len) if (end == -1) end = document.cookie.length return unescape (document.cookie.substring (len, end)) }, set: function (name, value, expires, path, domain, secure) { var expires_date = new Date() expires_date.setTime (expires_date.getTime() + expires * 1000) document.cookie = name + '=' + escape (value) + (expires? ';expires=' + expires_date.toGMTString() : '') + //expires.toGMTString() (path? ';path=' + path : '') + (domain? ';domain=' + domain : '') + (secure? ';secure' : '') }, del: function (name, path, domain) { if (Cookie.get (name)) document.cookie = name + '=' + (path? ';path=' + path : '') + (domain? ';domain=' + domain : '') + ';expires=Thu, 01-Jan-1970 00:00:01 GMT'; } }) var Tabber = Class.create ({ initialize: function (list) { this.links = [] if (list instanceof Array) list.each (function (pair) { this.add_tab (pair[0], pair[1], pair[2]) }.bind (this)) }, add_tab: function (link, content, onselect) { link = $(link) link.tabber = this link.tabber_content = $(content) link.tabber_show = function() { // Hide other tabs, show this: this.tabber.links.reject (function (link) { return link == this }.bind (this)).each (function (link) { link.tabber_content.hide() link.removeClassName ('active') }) this.tabber_content.show().addClassName ('active').blur() this.onselect && this.onselect.bind (this)() } link.onclick = function() { this.tabber_show() return false } link.onselect = onselect // Show if first tab, hide if another one: link.tabber_content[this.links.length? 'hide' : 'show']() this.links.push (link) } }) var Flash = Class.create ({ initialize: function (element) { this.element = $(element) if (!this.element) throw new String ("couldn't find flash container element") this.element.observe ('click', function() { this.hide() }.bind (this)) if (this.element.innerHTML != '') setTimeout (function() { this.show() }.bind (this), 250) }, notice: function (html_message) { this.element.update ("

#{html_message}

".interpolate ({ html_message: html_message })) this.show() }, warning: function (html_message) { this.element.update ("

#{html_message}

".interpolate ({ html_message: html_message })) this.show() }, show: function() { this.hide_timeout && clearTimeout (this.hide_timeout) this.element.hide() new Effect.Parallel ([ new Effect.BlindDown (this.element, { sync: true }), new Effect.Appear (this.element, { sync: true }) ]) this.hide_timeout = setTimeout (function() { this.hide() }.bind (this), 5000) }, hide: function() { new Effect.Parallel ([ new Effect.BlindUp (this.element, { sync: true }), new Effect.Fade (this.element, { sync: true }) ]) } }) var Momencik = Class.create ({ initialize: function (element, content) { this.content = content this.element = $(element) this.element.hide() }, wait: function (content) { if (content || this.content) this.element.update (coalesce (content, this.content)) this.element.show() }, done: function() { this.element.hide() if (this.content) this.element.update() } }) /** * Various Mapcia's functions. */ var Mapcia = { dom_loaded: function() { // Current mode: this.mode_object = null // Map: if ($('main_map')) { this.map = new Map ('main_map') this.places = new Mapcia.Places (this.map, 'sidebar') this.search = new Mapcia.Search (this.map, 'search_phrase', 'sidebar') this.tags = new Mapcia.Tags (this.map, 'sidebar') this.showall = new Mapcia.ShowAll (this.map, 'sidebar') // Map mode: if (['places#edit', 'places#update'].include (Mapcia.action)) this.set_mode (Mapcia.MODE_GEOCODE) else if (Mapcia.action == 'places#create') { var latlng = document.location.hash? document.location.hash.substr (1).split (',') : null this.set_mode (Mapcia.MODE_GEOCODE, { position: latlng? { ns: parseFloat (latlng[0]), ew: parseFloat (latlng[1]) } : undefined }) } else if (Mapcia.action == 'places#search') { if (this.search_mode == 'phrase' || this.search_mode == 'all') this.set_mode (Mapcia.MODE_SEARCH) else if (this.search_mode == 'tag') this.set_mode (Mapcia.MODE_TAGS) } GEvent.addListener (this.map.map, 'dragend', this.__map_moved.bind (this)) GEvent.addListener (this.map.map, 'zoomend', this.__map_moved.bind (this)) GEvent.addListener (this.map.map, 'moveend', this.__map_moved_by_internals.bind (this)) } // Flash messages: this.flash = new Flash ('flash') // Handling signin/up links: this.signer = new Mapcia.SignInSignUp ('signin-link', 'signup-link') }, set_link_to_this_page: function (link) { console.log ("Link to this page: " + link) }, new_place_at_latlng_url: function (latlng) { return "#{url}##{lat},#{lng}".interpolate ({ url: URL.place_create, lat: escape (latlng.lat()), lng: escape (latlng.lng()) }) }, new_place_at_latlng: function (latlng) { document.location = this.new_place_at_latlng_url (latlng) }, set_mode: function (mode, options) { options = options || {} this.places.cleanup() switch (this.mode) { case Mapcia.MODE_SHOW: case Mapcia.MODE_GEOCODE: this.map.hide_geocode_marker() break case Mapcia.MODE_SEARCH: this.search.leave() break case Mapcia.MODE_TAGS: this.tags.leave() break case Mapcia.MODE_SHOWALL: this.showall.leave() break } this.mode = mode switch (this.mode) { case Mapcia.MODE_SHOW: this.map.show_geocode_marker (false, options.position, false) this.mode_object = null break case Mapcia.MODE_GEOCODE: this.map.show_geocode_marker (true, options.position, options.reload_on_drop) break case Mapcia.MODE_SEARCH: this.mode_object = this.search this.mode_object.enter() break case Mapcia.MODE_TAGS: this.mode_object = this.tags this.mode_object.enter() break case Mapcia.MODE_SHOWALL: this.mode_object = this.showall this.mode_object.enter() break } }, show_indicator: function() { $$('#sidebar .content')[0].appendChild (new Element ('div', { 'class': 'big-waiting-indicator' })) }, xhr_failure: function (transport) { alert ('Komunikat serwera: #{response}'.interpolate ({ response: transport.responseText })) }, __map_moved_by_internals: function() { this.mode_object && this.mode_object.update_url() }, __map_moved: function (no_repeat) { this.showall.update() if (this.mode_object) { this.mode_object.update_url() this.mode_object.reload() } } } // Mapcia modes: Mapcia.MODE_SEARCH = 1 Mapcia.MODE_GEOCODE = 2 Mapcia.MODE_SHOW = 3 Mapcia.MODE_TAGS = 4 Mapcia.MODE_SHOWALL = 5 Mapcia.SignInSignUp = Class.create ({ initialize: function (signin_element, signup_element) { this.signin_element = $(signin_element) this.signup_element = $(signup_element) if (this.signin_element) this.signin_element.onclick = this.__signin.bind (this) if (this.signup_element) this.signup_element.onclick = this.__signup.bind (this) }, __signin: function() { // Otworzyć Blackboksa z formularzem logowania: this.blackbox = new Blackbox() this.blackbox.load_xhr (URL.user_login, { method: 'get' }, { afterLoad: function (container) { container.update (container.select ('.content')[0]) this.__xhrify_forms (container.select ('form')) }.bind (this) }) return false }, __signup: function() { // Otworzyć Blackboksa z formularzem rejestracji: this.blackbox = new Blackbox() this.blackbox.load_xhr (URL.user_register, { method: 'get' }, { afterLoad: function (container) { container.update (container.select ('.content')[0]) this.__xhrify_forms (container.select ('form')) }.bind (this) }) return false }, __xhrify_forms: function (forms) { forms.each (function (form) { form.onsubmit = this.__submit_form.bind (this) }.bind (this)) }, __submit_form: function (event) { var form = event.element() this.blackbox.load_xhr (form.action, { method: form.method, parameters: form.serialize() }, { afterLoad: function (container) { container.update (container.select ('.content')[0]) this.__xhrify_forms (container.select ('form')) }.bind (this) }) return false } }) Mapcia.Places = Class.create ({ initialize: function (map) { this.map = map if (!(this.map instanceof Map)) throw new String ('invalid argument for Mapcia.Places.initialize()') this.list = [] this.reload() }, cleanup: function() { //TODO this.map.remove_all_hover_titles() // Destroy current places: this.list.invoke ('destroy') }, reload: function() { this.cleanup() // Create new set of places: this.list = $$('.places .place').collect (function (element) { return new Mapcia.Places.Place (this, element) }.bind (this)) // If there is only one place, display marker popup: if (this.list.length == 1) this.list[0].marker.popup() }, adjust_map: function() { if (this.list.length == 0) return // Compute extents: var extents = { n: null, e: null, s: null, w: null, ns: 0, ew: 0 } this.list.each (function (place) { var ns = place.data.geo.ns var ew = place.data.geo.ew if (ns !== null && ew !== null) { extents.n = (extents.n === null)? ns : [extents.n, ns].max() extents.s = (extents.s === null)? ns : [extents.s, ns].min() extents.e = (extents.e === null)? ew : [extents.e, ew].max() extents.w = (extents.w === null)? ew : [extents.w, ew].min() } }) extents.ns = [extents.n - extents.s] extents.ew = [extents.e - extents.w] // Compute center: var center = { ns: (extents.n + extents.s) / 2.0, ew: (extents.e + extents.w) / 2.0 } // Compute zoom level: var zoom_level = 0 Map.ZOOM_TO_EXTENTS.clone().reverse().each (function (e, i) { if (e.ns > extents.ns && e.ew > extents.ew) { zoom_level = [Map.ZOOM_TO_EXTENTS.length - i, 14].min() throw $break } }) // Check if we need to zoom out one level, to ensure that // no place is too near to border: var c = 440.0 / 550.0 if (extents.ns > Map.ZOOM_TO_EXTENTS[zoom_level].ns * c || extents.ew > Map.ZOOM_TO_EXTENTS[zoom_level].ew * c) { zoom_level -= 1 } // Update map: this.map.map.setZoom (zoom_level) this.map.map.setCenter (new GLatLng (center.ns, center.ew)) } }) Mapcia.Places.Place = Class.create ({ initialize: function (places, element) { this.places = places this.element = element this.data = element.getAttribute ('data').evalJSON (false) this.info = "

#{name}

#{street}
#{postal_code}
#{city}
#{phone}
#{www}

".interpolate ({ name: h (coalesce (this.data.name, '')), street: h (coalesce (this.data.street, '')), postal_code: h (coalesce (this.data.postal_code, '')), city: h (coalesce (this.data.city, '')), phone: h (coalesce (this.data.phone, '')), www: h (coalesce (this.data.website, '')) }) this.link = element.select ('p.name a')[0] this.link.onclick = this.__link_click.bind (this) this.link.onmouseover = this.__link_mouseover.bind (this) this.link.onmouseout = this.__link_mouseout.bind (this) this.marker = this.places.map.create_marker (this.data.geo, this.info) this.marker.place = this this.marker.hover_title = h (this.data.name) + "
" + h (this.data.full_address) this.marker.popup = this.__marker_popup.bind (this) }, destroy: function() { this.places.map.destroy_marker (this.marker) }, hover: function (set) { this.element[set? 'addClassName' : 'removeClassName'] ('hover') }, activate: function() { this.places.list.each (function (place) { place.element.removeClassName ('active') place.marker.set_active (false) }) this.places.map.remove_all_hover_titles() // Reinsert marker, so zIndexProcess is recalled and marker is visible on top: this.places.map.map.removeOverlay (this.marker) this.places.map.map.addOverlay (this.marker) this.element.addClassName ('active') this.marker.set_active (true) }, __link_click: function() { this.activate() this.places.map.map.panTo (this.marker.getLatLng()) this.link.blur() return false }, __link_mouseover: function() { this.hover (true) this.marker.set_hovered (true) }, __link_mouseout: function() { this.hover (false) this.marker.set_hovered (false) }, __marker_popup: function() { // Smoothly scroll to place element: var p = $('place:' + this.data.id) var x = document.documentElement var delta = 175 if (this.places.scrolling_effect) this.places.scrolling_effect.cancel() this.places.scrolling_effect = new Effect.Tween (null, x.scrollTop + delta, p.offsetTop - x.offsetTop, { duration: 0.5 }, function (p) { x.scrollTop = p - delta }) this.activate() } }) Mapcia.Search = Class.create ({ initialize: function (map, input, target) { this.map = map this.input = $(input) this.target = $(target) // Bind events: //this.input.form.onsubmit = this.__submit_callback.bind (this) // If this is search result: if (Mapcia.action == 'places#search') Mapcia.places.adjust_map() this.last_queried_phrase = this.input.value }, enter: function() { this.__init_places() if (!Mapcia.restored && document.location.hash) { Mapcia.map.restore_geometry (document.location.hash) this.reload() Mapcia.restored = true } }, leave: function() { }, query: function (query_string, use_bbox) { if (this.query_request) this.query_request.transport.abort() this.last_queried_phrase = query_string this.query_request = new Ajax.Request (URL.place_search, { method: 'get', parameters: use_bbox? { phrase: query_string, bbox: $H(this.map.get_bbox()).toJSON() } : { phrase: query_string }, onSuccess: function (transport) { this.__load_places (transport.responseText) Mapcia.set_mode (Mapcia.MODE_SEARCH) }.bind (this), onFailure: Mapcia.xhr_failure }) return false }, query_by_url: function (url) { if (this.query_request) this.query_request.transport.abort() this.query_request = new Ajax.Request (url, { method: 'get', onSuccess: function (transport) { this.__load_places (transport.responseText) Mapcia.set_mode (Mapcia.MODE_SEARCH) }.bind (this), onFailure: Mapcia.xhr_failure }) return false }, reload: function() { this.prevent_adjusting_map = true return this.query (this.last_queried_phrase, true) }, update_url: function() { document.location.hash = Mapcia.map.dump_geometry() }, __submit_callback: function (event) { if (Mapcia.mode != Mapcia.MODE_SEARCH || !this.target) return true this.query (this.input.value) return false }, __load_places: function (markup) { this.target.update (markup) }, __init_places: function (markup) { Mapcia.places.reload() if (!this.prevent_adjusting_map) Mapcia.places.adjust_map() this.prevent_adjusting_map = false // XHRify pagination: if (false) { $$('.pages li a').each (function (a) { a.onclick = function() { return this.query_by_url (a.href) }.bind (this) }.bind (this)) } } }) Mapcia.Tags = Class.create ({ initialize: function (map, target) { this.map = map this.target = $(target) this.links = $$('.tagcloud a') this.links.each (function (link) { link.onclick = function() { this.current_link = link return this.reload() }.bind (this) }.bind (this)) this.current_link = null if (Mapcia.search_tag) { this.current_link = this.links.find (function (link) { return link.innerHTML = Mapcia.search_tag }) } }, enter: function() { this.__init_places() this.current_link.addClassName ('active') }, leave: function() { // Remove .active from tagcloud links: this.links.invoke ('removeClassName', 'active') }, query: function (tag_name) { if (this.query_request) this.query_request.transport.abort() this.query_request = new Ajax.Request (URL.place_search, { method: 'get', parameters: { tag: tag_name, bbox: $H(this.map.get_bbox()).toJSON() }, onSuccess: function (transport) { this.__load_places (transport.responseText) Mapcia.set_mode (Mapcia.MODE_TAGS) }.bind (this), onFailure: Mapcia.xhr_failure }) return false }, query_by_url: function (url) { if (this.query_request) this.query_request.transport.abort() this.query_request = new Ajax.Request (url, { method: 'get', onSuccess: function (transport) { this.__load_places (transport.responseText) Mapcia.set_mode (Mapcia.MODE_TAGS) }.bind (this), onFailure: Mapcia.xhr_failure }) return false }, reload: function() { return this.query (this.current_link.getAttribute ('tag_name')) }, update_url: function() { document.location.hash = Mapcia.map.dump_geometry() }, __load_places: function (markup) { this.target.update (markup) }, __init_places: function() { Mapcia.places.reload() // XHRify pagination: if (false) { $$('.pages li a').each (function (a) { a.onclick = function() { return this.query_by_url (a.href) }.bind (this) }.bind (this)) } } }) Mapcia.ShowAll = Class.create ({ initialize: function (map, target) { this.map = map this.target = $(target) this.button = $$('.show-all')[0] this.button.onclick = this.__button_click.bind (this) this.update() }, enter: function() { this.__init_places() }, leave: function() { }, update: function() { this.button.disabled = this.map.map.getZoom() < 14 }, query: function() { if (this.button.disabled) return if (this.query_request) this.query_request.transport.abort() this.query_request = new Ajax.Request (URL.place_search, { method: 'get', parameters: { all: true, bbox: $H(this.map.get_bbox()).toJSON() }, onSuccess: function (transport) { this.__load_places (transport.responseText) Mapcia.set_mode (Mapcia.MODE_SHOWALL) }.bind (this), onFailure: Mapcia.xhr_failure }) return false }, query_by_url: function (url) { if (this.button.disabled) return if (this.query_request) this.query_request.transport.abort() this.query_request = new Ajax.Request (url, { method: 'get', onSuccess: function (transport) { this.__load_places (transport.responseText) Mapcia.set_mode (Mapcia.MODE_SHOWALL) }.bind (this), onFailure: Mapcia.xhr_failure }) return false }, reload: function() { this.query() }, update_url: function() { document.location.hash = Mapcia.map.dump_geometry() }, __button_click: function() { return this.query() }, __load_places: function (markup) { this.target.update (markup) }, __init_places: function() { Mapcia.places.reload() // XHRify pagination: if (false) { $$('.pages li a').each (function (a) { a.onclick = function() { return this.query_by_url (a.href) }.bind (this) }.bind (this)) } } }) document.observe ('dom:loaded', Mapcia.dom_loaded.bind (Mapcia))