Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

« Newer Snippets
Older Snippets »
Showing 1-3 of 3 total  RSS 

Code to disable YUI calendar previous and next month links icons

This solution does not require modifying the yui source and works on YUI version 2.5.0. It will disable the next and previous arrows based on the out of bounds dates you specify with mindate and maxdate when you configure the calendar. Requires YUI dom and a namespace called your_app.

1) attach an onRender event when you configure the calendar and before you call render

calendar.renderEvent.subscribe(YAHOO.your_app.calendar().initArrows, calendar);


2) add the following function:

YAHOO.your_app.calendar = function() {
	
	var addDays = function(date, days) {
		return YAHOO.widget.DateMath.add(date, YAHOO.widget.DateMath.DAY, days);
	};
	
	var showPreviousArrow = function(cal) {
		return showArrow(cal, cal.toDate(cal.cellDates[0]), 1);
	};

	var showNextArrow = function(cal) {
		return showArrow(cal, cal.toDate(cal.cellDates[cal.cellDates.length-1]), -1);
	};
	
	var showArrow = function(cal, startingDate, step) {
		if (!cal.isDateOOM(startingDate)) { //ie not overlapping
			return !cal.isDateOOB(addDays(startingDate, (-1 * step)));
		}
		for (var i=0; (i * step) < 7; i += step) { //iterate forwards for previous month check, backwards for next month check
			var date = addDays(startingDate, i);
			if (!cal.isDateOOM(date)) { //shortcut exit; as soon as we find an in month date we can supress the arrow
				return false;
			} else if (!cal.isDateOOB(date)) {
				return true;
			}
		}
		return false;
	};
	
	return {
		initArrows: function(type, args, cal) {
			if (!showPreviousArrow(cal)) {
				hideArrow(cal, cal.Style.CSS_NAV_LEFT);
			}

			if (!showNextArrow(cal)) {
				hideArrow(cal, cal.Style.CSS_NAV_RIGHT);
			}
		}
	};
};

Implement Ruby String class prev / pred / prev! / pred! - opposite of next / succ methods

The Ruby String class includes the "next" function - also called "succ" for "successor" - which advances a string to the next logical item in, usually, an alphanumeric progression. For example, "9" goes to "10" goes to "11"; or "a" goes to "b" goes to "c". There is no opposite call in the standard String class. Searcing on the Web reveals that conventional wisdom regards this as difficult! It's certainly impossible to cover every case, since both "09".next and "9".next return "10", so going backwards from "10", it isn't possible to know whether to return "09" or "9".

Despite such difficulties, it's not actually that hard to make something that covers the vast majority of cases if you accept that it can't ever be perfect. The code below makes a very good job of returning, using "prev", to a string upon which "next" had been called. Extensive comments describe the implementation and its limitations.

class String
  # The opposite of String::next / String::succ. It is impossible to be a
  # *complete* opposite because both "9".next = "10" and "09".next = "10";
  # if going backwards from "10" there's no way to know whether the result
  # should be "09" or "9". Where the first ranged character is about to
  # underflow and the next character is within the same range the result
  # is shrunk down - that is, "10" goes to "9", "aa" goes to "z"; any non-
  # range prefix or suffix is OK, e.g. "+!$%10-=+" goes to "+!$%9-=+".
  # Items in the middle of a string don't do this - e.g. "12.10" goes to
  # "12.09", to match how "next" would work as best as possible.
  #
  # The standard "next" function works on strings that contain *no*
  # alphanumeric characters, using character codes. This implementation
  # of "prev" does *not* work on such strings - while strings may contain
  # any characters you like, only the alphanumeric components are operated
  # upon.
  #
  # Should total underflow result, "nil" will be returned - e.g. "00".prev
  # returns 'nil', as does "a".prev. This is done even if there are other
  # characters in the string that were not touched - e.g. "+0.0".prev
  # also returns "nil". Broadly speaking, a "nil" return value is used for
  # any attempt to find the previous value of a string that could not have
  # been generated using "next" in the first place.
  #
  # As with "next" sometimes the result of "prev" can be a little obscure
  # so it is often best to try out things using "irb" if unsure. Note in
  # particular that software revision numbers do not necessarily behave
  # predictably, because they don't with "next"! E.g. "12.4.9" might go to
  # "12.4.10" for a revision number, but "12.4.9".next = "12.5.0". Thus
  # "12.5.0".prev = "12.4.9" and "12.4.10".prev = "12.4.09" (because the
  # only way to make "12.4.10" using "next" is to start at "12.4.09").
  #
  # Since 'succ' (successor) is an alias for 'next', so 'pred'
  # (predecessor) is an alias for 'prev'.
  #
  def prev(collapse = false)
    str        = self.dup
    early_exit = false
    any_done   = false
    ranges     = [
                   ('0'[0]..'9'[0]),
                   ('a'[0]..'z'[0]),
                   ('A'[0]..'Z'[0]),
                   nil
                 ]

    # Search forward for the first in-range character. If found check
    # to see if that character is "1", "a" or "A". If it is, record
    # its index (from 0 to string length - 1). We'll need this if
    # underflows wrap as far as the found byte because in that case
    # this first found byte should be deleted ("aa..." -> "z...",
    # "10..." -> "9...").

    first_ranged = nil

    for index in (1..str.length)
      byte = str[index - 1]

      # Determine whether or not the current byte is a number, lower case
      # or upper case letter. We expect 'select' to only find one matching
      # array entry in 'ranges', thus we dereference index 0 after the
      # 'end' to put a matching range from within 'ranges' into 'within',
      # or 'nil' for any unmatched byte.

      within = ranges.select do |range|
        range.nil? or range.include?(byte)
      end [0]

      unless within.nil?
        case within.first
          when '0'[0]
            match_byte = '1'[0]
          else
            match_byte = within.first
        end

        first_ranged = index - 1 if (byte == match_byte)
        first_within = within
        break
      end
    end

    for index in (1..str.length)

      # Process the input string in reverse character order - fetch the
      # bytes via negative index.

      byte = str[-index]

      within = ranges.select do |range|
        range.nil? or range.include?(byte)
      end [0]

      # Skip this letter unless within a known range. Otherwise note that
      # at least one byte was able to be processed.

      next if within.nil?
      any_done = true

      # Decrement the current byte. If it is still within its range, set
      # the byte and bail out - we're finished. Flag the early exit. If
      # the byte is no longer within range, wrap the character around
      # and continue the loop to carry the decrement to an earlier byte.

      byte = byte - 1

      if (within.include? byte)
        str[-index] = byte
        early_exit  = true
        break
      else
        str[-index] = within.last

        # If we've just wrapped around a character immediately after the
        # one found right at the start ('0', 'a' or 'A') then this first
        # ranged character should be deleted (so "10" -> "09"

        if (first_ranged != nil and first_within.include?(byte + 1) and (first_ranged - str.length) == -(index + 1))
          str.slice!(-(index + 1))
          early_exit = true
          break
        end
      end

    end # From outer 'for' loop

    # If we did process at least one byte but we did not exit early, then
    # the loop completed due to carrying a decrement to other bytes. This
    # means an underflow condition - return 'nil'.

    if (any_done == true and early_exit == false)
      return nil
    else
      return str
    end
  end

  # As (extended) String::pred / String::prev, but modifies the string in
  # place rather than returning a copy. If underflow occurs, the string
  # will be unchanged. Returns 'self'.
  #
  def prev!
    new_str = prev
    self.replace(new_str) unless new_str.nil?
    return self
  end

  alias pred  prev
  alias pred! prev!

end


To prove it does an OK job, try some test code:

# Test code - create strings of random lengths between 1 and 16
# characters inclusive, including randomly selected characters from
# '0' to '9', 'a' to 'z' and 'A' to 'Z' inclusive. Check that 'next'
# then 'prev' returns to the original string and vice versa; ensure
# that the in-place functions ('!' variants) behave the same way as
# their transient counterparts and ensure that the aliases work too.
#
# Pass a number of iterations (that is, the number of random strings
# to generate).
#
def run_tests(iterations = 1000)
  puts "\n"
  puts "Running tests...\n"

  iterations.times do
    chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
    @newpass = ""
    len = rand(16)+1
    1.upto(len) { |i| @newpass << chars[rand(chars.size-1)] }

    next if @newpass.prev.nil?

    @passcp1 = @newpass.dup; @passcp1.prev!
    @passcp2 = @newpass.dup; @passcp2.pred!

    if (
         @newpass != @newpass.next.prev or @newpass != @newpass.prev.next or
         @newpass != @newpass.succ.pred or @newpass != @newpass.pred.succ or
         @newpass.prev != @passcp1 or
         @newpass.pred != @passcp2
       )

      puts "Failed on '#{@newpass}'\n"
      puts " with --> '#{@newpass}' vs '#{@newpass.next.prev}' vs '#{@newpass.prev.next}'\n"
      puts "   or --> '#{@newpass.prev}' vs '#{@passcp1}'\n"
      puts "   or --> '#{@newpass.pred}' vs '#{@passcp2}'\n"

      raise "Tests failed!"
    end
  end

  puts "Tests completed successfully.\n"
end

# Example call to test code.

run_tests(10000)

STEP - alpha-numeric string incrementer

    step: func [ ; INCR SUCC ADVANCE NEXT-STR, inspired by Ruby's succ method.
        "Increments strings, skipping non-alpha-num chars; doesn't auto-grow if first char is not alpha-num."
        series [string!]
        /local digit= alpha= alpha-num= =val carry? step-val mark
               range-start-char range-start-char? grow-if-necessary
    ][
        if empty? series [return series]
        digit=: charset [#"0" - #"9"]
        alpha=: charset [#"A" - #"Z" #"a" - #"z"]
        alpha-num=: union alpha= digit=
        carry?: does [find "9Zz" =val]
        range-start-char?: func [val] [find "0Aa" val]
        ; This gives us the new "rollover digit" to add, when we need
        ; to grow a string. Zeros rolling to ones is the exception to
        ; the rule; otherwise, we just add the same char that's there
        ; (the starting char for a range).
        ;
        range-start-char: func [val] [
            select/case [#"0" #"1"  #"A" #"A"  #"a" #"a"] val
        ]
        grow-if-necessary: does [
            if range-start-char? last series [
                append series range-start-char last series
            ]
        ]
        ; Increment a char val, rolling over if necessary.
        step-val: func [val] [
            either find "9Zz" val [
                select/case [#"9" #"0"  #"Z" #"A"  #"z" #"a"] val
            ] [add val 1]
        ]
        catch [
            ; Reverse the series, so we can parse left to right.
            parse/case reverse series [
                some [
                    mark:
                    copy =val alpha-num= (
                        change mark step-val to char! =val
                        ; Carry? means the char we just found will cause
                        ; a "carry" to the next digit, so we keep going;
                        ; otherwise, we're done
                        if not carry? [throw]
                    )
                    ; We just skip over non-alph-num chars in the current
                    ; design.
                    | skip
                ]
            ]
            ; If we stopped carrying at some point, we won't get here.
            grow-if-necessary
        ]
        ; We reversed the series to parse it, so reverse it again.
        reverse series
    ]
;     test-step: func [val expected-result] [
;         if expected-result <> step copy val [
;             print ["Test Failed:" mold val mold step copy val]
;         ]
;     ]
;     foreach [val res] [
;         ""    ""
;         "!"   "!"
;         "!@#$%^&*()" "!@#$%^&*()"
;         "aa"  "ab"
;         "aaa" "aab"
;         "az"  "ba"
;         "aZ"  "bA"
;         "zz"  "aaa"
;         "#zz" "#aa"
;         "ZZ"  "AAA"
;         "!ZZ" "!AA"
;         "001" "002"
;         "009" "010"
;         "099" "100"
;         "999"       "1000"
;         "~999"      "~000"
;         "123@999"   "124@000"
;         "1.2.3"     "1.2.4"
;         "001#1.2.9" "001#1.3.0"
;         "001#9.9.9" "002#0.0.0"
;         "001-zzz"   "002-aaa"
;     ] [test-step val res]
« Newer Snippets
Older Snippets »
Showing 1-3 of 3 total  RSS